{ "cells": [ { "cell_type": "markdown", "source": [ "# Stuff I Like About The Julia Programming Language" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "`git_club 2020-06-19` - Hannes" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "[View this notebook online](https://nbviewer.jupyter.org/github/Hasnep/stuff-i-like-about-julia/blob/master/stuff-i-like-about-julia.ipynb)" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "> Julia's unofficial tagline is \"Looks like Python, feels like Lisp, runs like Fortran.\"\n", "> I've been learning Julia for slightly less than a year now, and I'd like to share some of the things I've enjoyed about it.\n", "> I'll give an overview of the language's main features with code examples and discuss whether it really is the future scientific computing." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Technically:\n", "> Julia is a high level, JIT compiled, dynamic language designed with multiple dispatch, automatic differentiation and metaprogramming." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "In practice:\n", "> Julia finds a balance between being fast and easy to use with lots of features you'll miss when you use another language." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## History" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "- Julia was started in 2009 by Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and Alan Edelman\n", "- Announced in 2012\n", "- Version 1.0 was released in 2018\n", "- Now on version 1.4, heading for 1.5 soon" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Julia is easy to use" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Let's write a function from scratch to sum an array of numbers, first in Python:\n", "```python\n", "def my_sum(array):\n", " \"\"\"\n", " Sum a list.\n", " \"\"\"\n", " total = 0\n", " for x in array:\n", " total += x\n", " return total\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Then in Julia:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "Main.##302.my_sum" }, "metadata": {}, "execution_count": 1 } ], "cell_type": "code", "source": [ "\"\"\"\n", "Sum an array.\n", "\"\"\"\n", "function my_sum(array)\n", " total = 0\n", " for x in array\n", " total += x\n", " end\n", " return total\n", "end" ], "metadata": {}, "execution_count": 1 }, { "cell_type": "markdown", "source": [ "This syntax will be familliar if you've used Python." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "55" }, "metadata": {}, "execution_count": 2 } ], "cell_type": "code", "source": [ "my_sum(1:10)" ], "metadata": {}, "execution_count": 2 }, { "cell_type": "markdown", "source": [ "Benchmarks for this function in different languages:\n", "- C: ~10ms\n", "- Python: ~500ms\n", "- Julia: ~10ms\n", "> Source: [An Introduction to Julia (Beginner Level) | SciPy 2018 Tutorial | Jane Herriman, Sacha Verweij](https://www.youtube.com/watch?v=b5xvVyzUnXI)" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## It's fast" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Julia was designed to solve the \"two language problem\", where researchers use a slower, high-level language for research and then have to use a slower, low-level language once they hit a bottleneck or for production.\n", "Julia is both languages at the same time!" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Some benchmarks (from the Julia website) of different languages relative to C.\n", "![Benchmarks of different languages relative to C](https://julialang.org/assets/benchmarks/benchmarks.svg)\n", "> Source: https://julialang.org/benchmarks/" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "These benchmarks try to compare implementations of the same algorithm.\n", "Julia is comparable to compiled languages like Rust, Go, Fortran, etc. and is sometimes faster than C.\n", "Python is sometimes 100x slower than C.\n", "R is a bit slower than that." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "How is it so fast?" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Generally languages fall into two categories:" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "- Compiled languages like C or Rust compile all the code before you run\n", "- Interpreted languages like Python or R don't compile" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Julia uses a _Just-In-Time_ (JIT) compiler which compiles a function the first time it is called." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "In my opinion, speed doesn't matter all that much" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "- JIT compilation takes a short while\n", "- My time is more valuable than the computer's time" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Now, back to some code!" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Dynamically typed" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Let's write a function to calculate the $n$th Fibonacci number.\n", "We want the input `n` to only allow integers and we want to specify the output should only be integers." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "Main.##302.fib" }, "metadata": {}, "execution_count": 3 } ], "cell_type": "code", "source": [ "\"\"\"\n", "Calculate the nth Fibonacci number.\n", "\"\"\"\n", "function fib(n::Integer)::Integer\n", " if n < 2\n", " return n\n", " else\n", " return fib(n - 1) + fib(n - 2)\n", " end\n", "end" ], "metadata": {}, "execution_count": 3 }, { "cell_type": "markdown", "source": [ "If we try to run the function with a non-integer input" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "```julia\n", "julia> fib(0.5)\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Julia will throw an error:" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "```\n", "ERROR: MethodError: no method matching fib(::Float64)\n", "Closest candidates are:\n", " fib(::Integer)\n", "```" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "6765" }, "metadata": {}, "execution_count": 4 } ], "cell_type": "code", "source": [ "fib(20)" ], "metadata": {}, "execution_count": 4 }, { "cell_type": "markdown", "source": [ "There is also a one-line function notation that is useful for short functions" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "6765" }, "metadata": {}, "execution_count": 5 } ], "cell_type": "code", "source": [ "short_fib(n::Integer)::Integer = n < 2 ? n : short_fib(n - 1) + short_fib(n - 2)\n", "\n", "short_fib(20)" ], "metadata": {}, "execution_count": 5 }, { "cell_type": "markdown", "source": [ "## It's free!" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Julia is MIT licenced." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Broadcasting" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "In Python, most functions accept one element.\n", "Running this code:\n", "```python\n", ">>> import math\n", ">>> math.sin([1, 2, 3])\n", "```\n", "will give this error:\n", "```\n", "TypeError: must be real number, not list\n", "```\n", "Pythonistas would probably use a list comprehension:\n", "```python\n", ">>> [math.sin(x) for x in [1, 2, 3]]\n", "```\n", "or a map:\n", "```python\n", ">>> map(math.sin, [1, 2, 3])\n", "```\n", "```\n", "[0.8414709848078965, 0.9092974268256817, 0.1411200080598672]\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "In R, most functions are vectorised\n", "```R\n", "> sin(c(1, 2, 3))\n", "```\n", "```\n", "[1] 0.8414710 0.9092974 0.1411200\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "In Julia, functions are not vectorised, for example, this line:\n", "```julia\n", "julia> sin([1, 2, 3])\n", "```\n", "gives this error:\n", "```\n", "ERROR: MethodError: no method matching sin(::Array{Int64,1})\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Using the broadcast operator, the funciton is applied elementwise!" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "3-element Array{Float64,1}:\n 0.8414709848078965\n 0.9092974268256817\n 0.1411200080598672" }, "metadata": {}, "execution_count": 6 } ], "cell_type": "code", "source": [ "sin.([1, 2, 3])" ], "metadata": {}, "execution_count": 6 }, { "cell_type": "markdown", "source": [ "Broadcasting even works for user functions" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "10-element Array{Int64,1}:\n 1\n 1\n 2\n 3\n 5\n 8\n 13\n 21\n 34\n 55" }, "metadata": {}, "execution_count": 7 } ], "cell_type": "code", "source": [ "fib.(1:10)" ], "metadata": {}, "execution_count": 7 }, { "cell_type": "markdown", "source": [ "And for operators:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "10-element Array{Int64,1}:\n 1\n 4\n 9\n 16\n 25\n 36\n 49\n 64\n 81\n 100" }, "metadata": {}, "execution_count": 8 } ], "cell_type": "code", "source": [ "# What are the first 10 square numbers?\n", "(1:10).^2" ], "metadata": {}, "execution_count": 8 }, { "cell_type": "markdown", "source": [ "This is powerful, but sometimes tricky syntax.\n", "For example, adding a dot makes the length function broadcast over the array:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "7" }, "metadata": {}, "execution_count": 9 } ], "cell_type": "code", "source": [ "length(split(\"How many words are in this sentence?\"))" ], "metadata": {}, "execution_count": 9 }, { "cell_type": "markdown", "source": [ "+" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "8-element Array{Int64,1}:\n 3\n 4\n 10\n 3\n 4\n 2\n 5\n 6" }, "metadata": {}, "execution_count": 10 } ], "cell_type": "code", "source": [ "length.(split(\"How many characters are each of these words?\"))" ], "metadata": {}, "execution_count": 10 }, { "cell_type": "markdown", "source": [ "## Useful Unicode characters" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Julia lets you type lots of symbols using LaTeX-y abbreviations, e.g. type `\\alpha` and press tab for α or `\\sqrt` and tab for √." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "37" }, "metadata": {}, "execution_count": 11 } ], "cell_type": "code", "source": [ "α = 37" ], "metadata": {}, "execution_count": 11 }, { "cell_type": "markdown", "source": [ "The square root symbol is an abbreviation for the `sqrt()` function:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "6.082762530298219" }, "metadata": {}, "execution_count": 12 } ], "cell_type": "code", "source": [ "√α" ], "metadata": {}, "execution_count": 12 }, { "cell_type": "markdown", "source": [ "Some constants like π for pi and ℯ for Euler's constant are predefined:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "true" }, "metadata": {}, "execution_count": 13 } ], "cell_type": "code", "source": [ "# Use the approximate equals sign ≈ because this calculation is not exact\n", "ℯ^(im * π) ≈ -1" ], "metadata": {}, "execution_count": 13 }, { "cell_type": "markdown", "source": [ "You can define your own operators using either built in functions:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "true" }, "metadata": {}, "execution_count": 14 } ], "cell_type": "code", "source": [ "const ⊂ = issubset\n", "[2, 5] ⊂ [1, 2, 3, 4, 5]" ], "metadata": {}, "execution_count": 14 }, { "cell_type": "markdown", "source": [ "or user functions:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "55" }, "metadata": {}, "execution_count": 15 } ], "cell_type": "code", "source": [ "const ∑ = my_sum\n", "∑(1:10)" ], "metadata": {}, "execution_count": 15 }, { "cell_type": "markdown", "source": [ "Because Julia supports almost any symbol you can type as a variable name, that includes emojis!" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "true" }, "metadata": {}, "execution_count": 16 } ], "cell_type": "code", "source": [ "🔥 = 10\n", "🐶 = 20\n", "🌭 = 30\n", "🔥 + 🐶 == 🌭" ], "metadata": {}, "execution_count": 16 }, { "cell_type": "markdown", "source": [ "## Julia is (mostly) written in Julia" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "If you can read Julia code, you can also read Julia's source code to understand what it does.\n", "I looked at the most recent PR as an example:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "tensor (generic function with 1 method)" }, "metadata": {}, "execution_count": 17 } ], "cell_type": "code", "source": [ "tensor(A::AbstractArray, B::AbstractArray) = [a * b for a in A, b in B]\n", "const ⊗ = tensor" ], "metadata": {}, "execution_count": 17 }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "2×3 Array{Int64,2}:\n 3 4 5\n 6 8 10" }, "metadata": {}, "execution_count": 18 } ], "cell_type": "code", "source": [ "[1, 2] ⊗ [3, 4, 5]" ], "metadata": {}, "execution_count": 18 }, { "cell_type": "markdown", "source": [ "Python's numpy is fast because it's mostly written in C/C++ (51.4%), but if you want to do something that numpy can't do, you need to either use C++ or write slower Python code." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "This bridges the gap between a Julia user and a Julia developer.\n", "For every user of Julia, there's another possible contributer." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Multiple dispatch" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "As an example, let's build a small Julia package based on [Measurements.jl](https://github.com/JuliaPhysics/Measurements.jl/)." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "Main.##302.Uncertain" }, "metadata": {}, "execution_count": 19 } ], "cell_type": "code", "source": [ "\"\"\"\n", "A number with some error.\n", "\"\"\"\n", "struct Uncertain <: Real\n", " val::Real\n", " err::Real\n", "end" ], "metadata": {}, "execution_count": 19 }, { "cell_type": "markdown", "source": [ "Define the standard gravity on earth." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "Main.##302.Uncertain(9.8, 0.1)" }, "metadata": {}, "execution_count": 20 } ], "cell_type": "code", "source": [ "g = Uncertain(9.8, 0.1)" ], "metadata": {}, "execution_count": 20 }, { "cell_type": "markdown", "source": [ "Wouldn't it be nicer to show an uncertain number with the plus/minus symbol?\n", "Let's write a show function that dispatches on the Uncertain type:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "9.8 ∓ 0.1" }, "metadata": {}, "execution_count": 21 } ], "cell_type": "code", "source": [ "Base.show(io::IO, x::Uncertain) = print(io, \"$(x.val) ∓ $(x.err)\")\n", "\n", "g" ], "metadata": {}, "execution_count": 21 }, { "cell_type": "markdown", "source": [ "Let's define the plus/minus operator to create `Uncertain` numbers:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "190 ∓ 1" }, "metadata": {}, "execution_count": 22 } ], "cell_type": "code", "source": [ "∓(a::Real, b::Real) = Uncertain(a, b)\n", "my_height = 190 ∓ 1" ], "metadata": {}, "execution_count": 22 }, { "cell_type": "markdown", "source": [ "How do you add two uncertain measurements?" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "175 ∓ 2" }, "metadata": {}, "execution_count": 23 } ], "cell_type": "code", "source": [ "my_brothers_height = 175 ∓ 2" ], "metadata": {}, "execution_count": 23 }, { "cell_type": "markdown", "source": [ "```julia\n", "julia> my_height + my_brothers_height\n", "```\n", "```\n", "ERROR: + not defined for Uncertain\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "$$\n", "Q = a + b \\\\\n", "{\\delta Q} = \\sqrt{(\\delta a)^2 + (\\delta b)^2}\n", "$$" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "365 ∓ 2.23606797749979" }, "metadata": {}, "execution_count": 24 } ], "cell_type": "code", "source": [ "Base.:+(a::Uncertain, b::Uncertain) = (a.val + b.val) ∓ sqrt(a.err^2 + b.err^2)\n", "my_height + my_brothers_height" ], "metadata": {}, "execution_count": 24 }, { "cell_type": "markdown", "source": [ "Similar for subtraction." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "15 ∓ 2.23606797749979" }, "metadata": {}, "execution_count": 25 } ], "cell_type": "code", "source": [ "Base.:-(a::Uncertain, b::Uncertain) = (a.val - b.val) ∓ sqrt(a.err^2 + b.err^2)\n", "my_height - my_brothers_height" ], "metadata": {}, "execution_count": 25 }, { "cell_type": "markdown", "source": [ "Slightly more complicated for multiplcation and division." ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "function Base.:*(a::Uncertain, b::Uncertain)\n", " total_value = a.val * b.val\n", " total_error = total_value * sqrt((a.err / a.val)^2 + (b.err / b.val)^2)\n", " return Uncertain(total_value, total_error)\n", "end\n", "\n", "function Base.:/(a::Uncertain, b::Uncertain)\n", " total_value = a.val / b.val\n", " total_error = total_value * sqrt((a.err / a.val)^2 + (b.err / b.val)^2)\n", " return Uncertain(total_value, total_error)\n", "end" ], "metadata": {}, "execution_count": 26 }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "33250 ∓ 418.35989291517893" }, "metadata": {}, "execution_count": 27 } ], "cell_type": "code", "source": [ "my_height * my_brothers_height" ], "metadata": {}, "execution_count": 27 }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "1.0857142857142856 ∓ 0.013660731197230332" }, "metadata": {}, "execution_count": 28 } ], "cell_type": "code", "source": [ "my_height / my_brothers_height" ], "metadata": {}, "execution_count": 28 }, { "cell_type": "markdown", "source": [ "Finally powers, again the exact formula is not important." ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "36100.0 ∓ 380.0" }, "metadata": {}, "execution_count": 29 } ], "cell_type": "code", "source": [ "Base.:^(a::Uncertain, b::Real) = (a.val^b) ∓ (abs(b) * a.val^(b - 1) * a.err)\n", "my_height^2.0" ], "metadata": {}, "execution_count": 29 }, { "cell_type": "markdown", "source": [ "Finally, we need to tell Julia what to do if we give it an `Uncertain` number and some other number in the same operation." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "```julia\n", "julia> 1 + g\n", "```\n", "```\n", "ERROR: promotion of types Int64 and Uncertain failed to change any arguments\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "We want to convert both numbers to our `Uncertain` type:" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "Base.promote_rule(::Type{Uncertain}, ::Type{T}) where T <: Real = Uncertain" ], "metadata": {}, "execution_count": 30 }, { "cell_type": "markdown", "source": [ "Coverting a real number to our Uncertain type just means we add an error of 0:" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "Base.convert(::Type{Uncertain}, x::Real) = Uncertain(x, 0)" ], "metadata": {}, "execution_count": 31 }, { "cell_type": "markdown", "source": [ "When Julia wants to convert an Uncertain number it doesn't need to do anything :" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "Base.convert(::Type{Uncertain}, x::Uncertain) = x" ], "metadata": {}, "execution_count": 32 }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "10.8 ∓ 0.1" }, "metadata": {}, "execution_count": 33 } ], "cell_type": "code", "source": [ "1 + g" ], "metadata": {}, "execution_count": 33 }, { "cell_type": "markdown", "source": [ "Now we can solve a simple problem, how much time would it take if I dropped a ball from my height and my brother caught it at his height?" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Solve for t:\n", "$$\n", "t = \\frac{\\sqrt{2 a s + u^2} - u}{a} = \\frac{\\sqrt{2 g (h_1 - h_2)}}{g}\n", "$$" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "1.749635530559413 ∓ 0.13192889617888173" }, "metadata": {}, "execution_count": 34 } ], "cell_type": "code", "source": [ "t = ((2 * g * (my_height - my_brothers_height))^0.5) / g" ], "metadata": {}, "execution_count": 34 }, { "cell_type": "markdown", "source": [ "Let's use the actual Measurements.jl package" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "using Measurements: Measurement, ±" ], "metadata": {}, "execution_count": 35 }, { "cell_type": "markdown", "source": [ "Let's solve the same problem as before and make sure we get the same result:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "1.75 ± 0.13", "text/latex": "$1.75 \\pm 0.13$" }, "metadata": {}, "execution_count": 36 } ], "cell_type": "code", "source": [ "g = 9.8 ± 0.1\n", "my_height = 190 ± 1\n", "my_brothers_height = 175 ± 2\n", "\n", "t = (2 * g * (my_height - my_brothers_height))^0.5 / g" ], "metadata": {}, "execution_count": 36 }, { "cell_type": "markdown", "source": [ "This next part is inspired by a JuliaCon talk called [_The Unreasonable Effectiveness of Multiple Dispatch_](https://www.youtube.com/watch?v=kc9HwsxE1OY) by one of the Julia co-founders, Stefan Karpinski." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "All the \"methods\" are outside the class definition, that can include being in a completely different package." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Let's solve the same problem using differential equations!" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Calculate the position of the ball between time $t = 0$ and $t = 3$\n", "$$\n", "v = \\frac{ds}{dt}\n", "$$\n", "$$\n", "f(t) = -g t = - (9.8 \\pm 0.1) t\n", "$$\n", "With initial conditions $s_0 = 190 \\pm 1$" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "using DifferentialEquations" ], "metadata": {}, "execution_count": 37 }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "\u001b[36mODEProblem\u001b[0m with uType \u001b[36mMeasurements.Measurement{Float64}\u001b[0m and tType \u001b[36mFloat64\u001b[0m. In-place: \u001b[36mfalse\u001b[0m\ntimespan: (0.0, 3.0)\nu0: 190.0 ± 1.0" }, "metadata": {}, "execution_count": 38 } ], "cell_type": "code", "source": [ "f(s, p, t) = -g * t\n", "s₀ = my_height\n", "time_span = (0.0, 3.0)\n", "problem = ODEProblem(f, s₀, time_span)" ], "metadata": {}, "execution_count": 38 }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "retcode: Success\nInterpolation: 1st order linear\nt: 31-element Array{Float64,1}:\n 0.0\n 0.1\n 0.2\n 0.3\n 0.4\n 0.5\n 0.6\n 0.7\n 0.8\n 0.9\n ⋮\n 2.2\n 2.3\n 2.4\n 2.5\n 2.6\n 2.7\n 2.8\n 2.9\n 3.0\nu: 31-element Array{Measurements.Measurement{Float64},1}:\n 190.0 ± 1.0\n 190.0 ± 1.0\n 189.8 ± 1.0\n 189.6 ± 1.0\n 189.2 ± 1.0\n 188.8 ± 1.0\n 188.2 ± 1.0\n 187.6 ± 1.0\n 186.9 ± 1.0\n 186.0 ± 1.0\n ⋮\n 166.3 ± 1.0\n 164.1 ± 1.0\n 161.8 ± 1.0\n 159.4 ± 1.0\n 156.9 ± 1.1\n 154.3 ± 1.1\n 151.6 ± 1.1\n 148.8 ± 1.1\n 145.9 ± 1.1" }, "metadata": {}, "execution_count": 39 } ], "cell_type": "code", "source": [ "solution = solve(problem, Tsit5(), saveat=0.1)" ], "metadata": {}, "execution_count": 39 }, { "outputs": [], "cell_type": "code", "source": [ "using Plots" ], "metadata": {}, "execution_count": 40 }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "Plot{Plots.GRBackend() n=1}", "image/png": "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", "text/html": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ], "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "metadata": {}, "execution_count": 41 } ], "cell_type": "code", "source": [ "plot(\n", " solution.t,\n", " solution.u,\n", " title=\"Solution to the ODE\",\n", " xaxis=\"Time (t) in seconds\",\n", " yaxis=\"Displacement s(t) in metres\"\n", ")" ], "metadata": {}, "execution_count": 41 }, { "cell_type": "markdown", "source": [ "## Interoperability" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "We can import any python module using the PyCall package:" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "using PyCall" ], "metadata": {}, "execution_count": 42 }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "PyObject " }, "metadata": {}, "execution_count": 43 } ], "cell_type": "code", "source": [ "math = pyimport(\"math\")" ], "metadata": {}, "execution_count": 43 }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "10.0" }, "metadata": {}, "execution_count": 44 } ], "cell_type": "code", "source": [ "math.sqrt(100)" ], "metadata": {}, "execution_count": 44 }, { "cell_type": "markdown", "source": [ "We can mix Python functions and variables with Julia code:" ], "metadata": {} }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "4-element Array{Float64,1}:\n 1.2246467991473532e-16\n 1.2246467991473532e-16\n -2.4492935982947064e-16\n -2.4492935982947064e-16" }, "metadata": {}, "execution_count": 45 } ], "cell_type": "code", "source": [ "math.sin.([π, math.pi, 2π, 2 * math.pi])" ], "metadata": {}, "execution_count": 45 }, { "cell_type": "markdown", "source": [ "We can define Python functions that call Julia funcitons" ], "metadata": {} }, { "outputs": [], "cell_type": "code", "source": [ "py\"\"\"\n", "def pyfib(n, fun):\n", " if n < 2:\n", " return n\n", " else:\n", " return fun(n - 1, pyfib) + fun(n - 2, pyfib)\n", "\"\"\"" ], "metadata": {}, "execution_count": 46 }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "jlfib (generic function with 1 method)" }, "metadata": {}, "execution_count": 47 } ], "cell_type": "code", "source": [ "function jlfib(n, fun)\n", " if n < 2\n", " return n\n", " else\n", " return fun(n - 1, jlfib) + fun(n - 2, jlfib)\n", " end\n", "end" ], "metadata": {}, "execution_count": 47 }, { "outputs": [ { "output_type": "execute_result", "data": { "text/plain": "6765" }, "metadata": {}, "execution_count": 48 } ], "cell_type": "code", "source": [ "jlfib(20, py\"pyfib\")" ], "metadata": {}, "execution_count": 48 }, { "cell_type": "markdown", "source": [ "If you have some code written in another language (like Python), there can be a smooth transition to using Julia." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "# Summary" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "- Julia looks a bit like Python\n", "- It's fast (with some caveats)\n", "- It's free!\n", "- It's written in Julia\n", "- It has powerful features like:\n", " - Broadcasting syntax\n", " - Unicode variables\n", " - Multiple dispatch\n", " - Good interoperability" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Other stuff I haven't mentioned:\n", "- Automatic differentiation (Swift also has this now)\n", "- Metaprogramming\n", "- Data science stuff (The first two letters of Jupyter Notebooks are named after Julia!)" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "---\n", "\n", "*This notebook was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*" ], "metadata": {} } ], "nbformat_minor": 3, "metadata": { "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.4.1" }, "kernelspec": { "name": "julia-1.4", "display_name": "Julia 1.4.1", "language": "julia" } }, "nbformat": 4 }