This is one of the 100 recipes of the IPython Cookbook, the definitive guide to high-performance scientific computing and data science in Python.

5.12. Trying the Julia language in the notebook

For this recipe, you need to install Julia and IJulia. You'll find the installation instructions in the book.

  1. We can't avoid the customary Hello World example. The println() function displays a string and adds a line break at the end.
In [ ]:
println("Hello world!")
  1. We create a polymorphic function f that computes the expression $z*z+c$. We will notably evaluate this function on arrays, so we use elementwise operators with a dot (.) prefix.
In [ ]:
f(z, c) = z.*z .+ c
  1. Let's evaluate f on scalar complex numbers (the imaginary number $i$ is 1im).
In [ ]:
f(2.0 + 1.0im, 1.0)
  1. Now, we create a (2, 2) matrix. Components are separated by a space, rows are separated by a semicolon (;). The type of this Array is automatically inferred from its components. The Array type is a built-in data type in Julia, similar, but not identical, to NumPy's ndarray type.
In [ ]:
z = [-1.0 - 1.0im  1.0 - 1.0im;
     -1.0 + 1.0im  1.0 + 1.0im]
  1. We can index arrays with brackets []. A notable difference with Python is that indexing starts from 1 instead of 0. MATLAB has the same convention. Besides, the keyword end refers to the last item in that dimension.
In [ ]:
  1. We can evaluate f on the matrix z and a scalar c (polymorphism).
In [ ]:
f(z, 0)
  1. Now, we create a function julia that computes a Julia set. Optional named arguments are separated from positional arguments by a semicolon (;). Julia's syntax for flow control is close from Python's, except that colons are dropped, indentation doesn't count, and block end keywords are mandatory.
In [ ]:
function julia(z, c; maxiter=200)
    for n = 1:maxiter
        if abs2(z) > 4.0
            return n-1
        z = f(z, c)
    return maxiter
  1. We can use Python packages from Julia. First, we have to install the PyCall package by using Julia's built-in package manager (Pkg). Once the package is installed, we can use it in the interactive session with using PyCall.
In [ ]:
using PyCall
  1. We can import Python packages with the @pyimport macro (a metaprogramming feature in Julia). This macro is the equivalent of Python's import command.
In [ ]:
@pyimport numpy as np
  1. The np namespace is now available in the Julia interactive session. NumPy arrays are automatically converted to Julia Arrays.
In [ ]:
z = np.linspace(-1., 1., 100)
  1. We can use list comprehensions to evaluate the function julia on many arguments.
In [ ]:
m = [julia(z[i], 0.5) for i=1:100]
  1. Let's try the Gadfly plotting package. This library offers a high-level plotting interface inspired by Grammar of Graphics. In the notebook, plots are interactive thanks to the d3.js library.
In [ ]:
using Gadfly
In [ ]:
plot(x=1:100, y=m, Geom.point, Geom.line)
  1. Now, we compute a Julia set by using two nested loops. In general, and unlike Python, there is no significant performance penalty using for loops instead of vectorized operations in Julia. High-performance code can be written either with vectorized operations or for loops.
In [ ]:
@time m = [julia(complex(r, i), complex(-0.06, 0.67)) 
           for i = 1:-.001:-1,
               r = -1.5:.001:1.5];
  1. Finally, we use the PyPlot package to draw matplotlib figures in Julia.
In [ ]:
using PyPlot
In [ ]:
imshow(m, cmap="RdGy", 
       extent=[-1.5, 1.5, -1, 1]);

You'll find all the explanations, figures, references, and much more in the book (to be released later this summer).

IPython Cookbook, by Cyrille Rossant, Packt Publishing, 2014 (500 pages).