{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Julia: A numerical programming language" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Github:** [mfherbst](https://github.com/mfherbst) \n", "**EMail:** michael.herbst@enpc.fr \n", "**Web:** " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Overview\n", "\n", "- Very recent: v1.0. released August 2018\n", "- Compiled scripting language\n", "- JIT with LLVM backend\n", "- High-level syntax, dynamical\n", "- Garbage collected\n", "- Strongly typed\n", "- A bit like `python` with integrated `numpy`\n", "\n", "\n", "- **Multiple dispatch**\n", "- Rich interoperability: `Fortran`, `C`, `C++`, `python`, `R`, ...\n", "- Tries to facilitate modern developments:\n", " - Machine learning\n", " - Large-scala data analysis\n", " - Automatic differentiation)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Functions and multiple dispatch " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "function mysquare(x)\n", " x * x\n", "end\n", "\n", "# or alternatively just:\n", "mysquare(x) = x * x" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "mysquare(3) # Integer arithmetic" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mysquare(2im) # Complex arithmetic" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Lists == Arrays == Vectorisation" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "typeof([\"abc\"])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "typeof([1, 2, 3])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "typeof([1.1, 2, 3])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "typeof([\"abc\", 1, 2.3])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "arr = [1.1, 2, 3]\n", "\n", "mysquare.(arr) # Vectorised call" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sin.(exp.(mysquare.(arr)))\n", "\n", "# or:\n", "@. sin(exp(mysquare(arr)))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "using Printf\n", "\n", "function printarray(array::Array{T, 1}) where T\n", " println(\"General array passed, elements:\")\n", " for element in array\n", " println(\" $element\")\n", " end\n", "end\n", "\n", "function printarray(array::Array{T, 1}) where T <: Number\n", " println(\"Numeric array passed, elements:\")\n", " for element in array\n", " println(\" $element\")\n", " end\n", "end\n", "\n", "function printarray(array::Array{Float64, 1})\n", " println(\"Float array passed, elements:\")\n", " for element in array\n", " @printf \" %10.5f\\n\" element\n", " end\n", "end\n", "\n", "printarray([\"a\", \"b\", 1])\n", "println()\n", "printarray([1, 2, 3])\n", "println()\n", "printarray([1., 2., 3.])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Changing precision" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Type = Float16\n", "\n", "a = rand(Type, 3, 4)\n", "sum(mysquare.(a))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Changing computational backend" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "using StaticArrays\n", "\n", "# Array operations on static-sized arrays:\n", "a = @SArray[1,2,3,4]\n", "mysquare.(a)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "using SparseArrays\n", "\n", "# random sparse array\n", "a = sprandn(6, 6, 0.3)\n", "mysquare.(a)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "using Distributed\n", "\n", "# Add 4 processes and setup environment at each of them\n", "#addprocs(4)\n", "\n", "@everywhere begin\n", " mysquare(x) = x * x\n", " using DistributedArrays\n", "end\n", "\n", "a = drandn(100,100,10)\n", "println(sum(mysquare.(a)))\n", "close(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Structs and inheritance\n", "- A struct is a collection of data" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "struct MyPoint\n", " x::Float64\n", " y::Float64\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Note:** A struct contains no methods and is *immutable* by default." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p = MyPoint(1, 2)\n", "p.x = 6" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Functionality is provided by *external* functions only:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pointlength(p::MyPoint) = sqrt(p.x^2 + p.y^2)\n", "pointlength(p)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Abstract types are only names to \"group\" concrete structs:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "using StaticArrays\n", "\n", "@show AbstractArray{Float64} <: AbstractArray\n", "@show SArray <: AbstractArray\n", "\n", "@show Array{Float64} <: AbstractArray{Float64}\n", "@show Array{Int64} <: AbstractArray{Float64};" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Function, which works on all Abstract Arrays of element type Float64\n", "function strangesum(a::AbstractArray{Float64})\n", " a[1] + sum(a[2:end])\n", "end" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# ok:\n", "strangesum([1., 2., 3.])\n", "strangesum(@SArray[1., 2., 3.])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# not ok:\n", "strangesum([1, 2, 3])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Automatic differentiation (Adjoint mode)\n", "\n", "- FD and Forward well-established\n", "- Adjoint mode: Many packages, Zygote very promising, but experimental" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "using Zygote\n", "using SpecialFunctions\n", "\n", "f(x) = 2x*sin(x) - x^2/2 " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@time f'(1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@time f'(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Integration with existing scientific codes\n", "- e.g. `python`, `C++`, `Fortan`, `R`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Call to lapack to compute dot product\n", "v = [1.0, 2.0, 3.0, 4.0]\n", "w = [2.0, 3.0, 4.0, 5.0]\n", "\n", "VdotW = ccall((:ddot_, \"liblapack\"), Float64,\n", " (Ref{Int32}, Ptr{Float64}, Ref{Int32}, Ptr{Float64}, Ref{Int32}),\n", " length(v), v, 1, w, 1)\n", "println(\"v ⋅ w = $VdotW\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Plot function in matplotlib\n", "using PyPlot\n", "\n", "# f defined as above\n", "x = collect(-4:0.2:7)\n", "PyPlot.plot(x, f.(x), label=\"f(x) = 2x*sin(x) - x^2/2 \")\n", "PyPlot.plot(x, f'.(x), label=\"f'(x)\")\n", "PyPlot.axhline(0, color=\"grey\", linewidth=0.5)\n", "PyPlot.legend()\n", "PyPlot.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## A word about performance\n", "\n", "- Best out of five\n", "- Heat equation example (based on code samples by Antoine Levitt)\n", "- Used software: gcc 8.3, clang 7.0.1, python 3.7, numpy 1.16.2, julia 1.0.3\n", "\n", "```\n", " | | duration (s)\n", "-------- | ------------------------- | ---------------\n", "python | array operation (numpy) | 11.8\n", " | | \n", "C | gcc | 8.1\n", "C | gcc -O3 | 1.1\n", "C | gcc -O3 -march=native | 0.5\n", " | | \n", "C | clang | 8.0\n", "C | clang -O3 | 1.1\n", "C | clang -O3 -march=native | 0.8\n", " | | \n", "julia | array operations | 3.3\n", "julia | loops | 1.9\n", "julia | loops, no bounds check | 0.5\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Wrapping up\n", "- Stronger emphasis on functional-style programming\n", "- Multiple dispatch based on JIT-compiling and strong typing\n", "\n", "\n", "- Two-way interoperability with Fortran, C / C++, python, R\n", "- Threading, vectorisation and distributed memory in high-level syntax\n", "\n", "\n", "- Uses cases:\n", " - Great language for algorithmic developments\n", " - High-level alternative for `Fortran` and `C` in high-performance computing\n", " - Helpful for one-shot scripts (easy parallelisation)\n", " - Not so much for classical scripting (glue code)" ] } ], "metadata": { "kernelspec": { "display_name": "Julia 1.0.3", "language": "julia", "name": "julia-1.0" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.0.3" } }, "nbformat": 4, "nbformat_minor": 2 }