Lightning Julia

Samuel Colvin

 

 
(You can swap **Lisp** for **Julia** and **Perl** for **Python**.)

What is Julia?

  • Julia is a high-level, high-performance dynamic programming language for technical computing.

  • It looks like a cross between Matlab and python, with types like c and macros like lisp.

  • Is GIT compiled using LLVM.

  • Is Function oriented (eg. no classes like Python), uses multiple dispatch to be flexible while still very fast.

  • Is free, open source and released under the MIT license (mostly). It's primary developers are academics at MIT.

So What's the Point?

Julia is Fast. Really Fast!

Being Fast has secondary advantages: You can read the sourcecode for libraries (and contribute to them without having to learn c.

But that's not all:

  • Simply maths syntax (2π*r^2) and matrix syntax (a = [sin(x) cos(x); tan(x) 1])
  • Batteries included:
    • Package manager.
    • Support for parallelism.
    • The shell (REPL) is interactive (eg. like ipython).
    • Pretty printing.
  • A lot of effort has gone into get Julia to compile easily in Mac, Linux and even Windows.
  • Unicode source and support for output in rich multimedia types.
  • Small but very active and helpful user/developer base.
    • If there's a feature you want, or you find a bug you can discuss it directly with the core developers.
  • And this: IJulia - IPython notebook integration.

Speed


In [9]:
function fib(n) 
    if n < 2 
        return n
    else
        return fib(n-1) + fib(n-2)
    end
end
@time fib(36)


elapsed time: 0.155320374 seconds (35416 bytes allocated)
Out[9]:
14930352

In Python:

from time import time
def fib(n):
    if n < 2:
        return n
    else:
        return fib(n-1) + fib(n-2)
start = time()
f=fib(36)
stop = time()
print('time taken: %0.4f' % (stop - start))
print('result: %d' % f)

  • Julia 0.3 latest: 0.1204 (used in test)
  • python2.7: 3.6751 (30x slower)
  • python3.4: 4.8916 (40x slower)
  • pypy 2.7: 0.6789 (5.6x slower)
  • cython (no changes): 2.3770 (19x slower)
  • cython (type set): 1.9122 (16x slower)

What's the snag?

We have to remember that Julia is still very young.

  • Libaries: nowhere near as many libraries/packages as in Python/R/Matlab.
    • However:
      • Can call Python/R/c easily from Julia.
      • Libraries are coming thick and fast.
  • IO still slow:
    • CSV and JSON input and output is still slowing than python.
    • No MySQL library, ODBC library buggy
    • However:
      • good HDF5 support with custom "jld" format for persistence.
  • Still a language geek's language:

Any Questions?

Acknowledgements

Avik Sengupta for an example of using IJulia for presenations

Stefan, with Jeff Bezanson, Viral B Shah, Alan Edelman and everyone else for creating Julia


In [1]:
using Bokeh
setupnotebook()


BokehJS successfully loaded.


In [2]:
abstract Animal

midrand() = rand() - 0.5
prednames = [:Tiger, :Lion, :Eagle]
type Preditor <: Animal
    breed::Symbol
    alive::Bool
    x::Float64; y::Float64
    heading::Float64; speed::Float64
    route::Vector{(Float64, Float64)}
    eaten::Int
    Preditor(breed::Symbol) = new(breed, true, 5midrand(), 5midrand(), 
        rand()*2pi, 0, Array((Float64, Float64), 0), 0)
end
function Preditor()
    Preditor(prednames[rand(1:length(prednames))])
end

preynames = [:Goat, :Sheep, :Horse, :Deer, :Chicken]
type Prey <: Animal
    breed::Symbol
    alive::Bool
    x::Float64; y::Float64
    heading::Float64; speed::Float64
    route::Vector{(Float64, Float64)}
    Prey(breed::Symbol) = new(breed, true, 5midrand(), 5midrand(),
                    rand()*2pi, 0, Array((Float64, Float64), 0))
end
function Prey()
    Prey(preynames[rand(1:length(preynames))])
end

function Base.show(io::IO, an::Animal)
    coords = @sprintf "%0.3f, %0.3f" an.x an.y
    print(io, "$(an.breed) ($coords)")
end

type Meeting
    event::Symbol
    x::Float64
    y::Float64
end

In [6]:
const area = 6
function move!(an::Animal)
    an.heading += midrand()
    an.speed += 0.01 + 0.001midrand()
    an.x += an.speed * sin(an.heading)
    an.y += an.speed * cos(an.heading)
    push!(an.route, (an.x, an.y))
    if abs(an.x) > area || abs(an.y) > area
        an.speed *= 0.1
    end
end

function move!(animals::Array{Animal, 1})
    for animal in animals
        move!(animal)
    end
end

distance(a1::Animal, a2::Animal) = sqrt((a1.x-a2.x)^2 + (a1.y-a2.y)^2)
const meetdist = 0.3
function meet!(animals::Array{Animal, 1}, meetings::Array{Meeting, 1})
    newanimals = Animal[]
    for (i, a1) in enumerate(animals)
        !a1.alive && continue
        for a2 in animals[(i+1):end]
            !a2.alive && continue
            distance(a1, a2) > meetdist && continue
            if a1.breed == a2.breed && length(animals) < 1000 && rand() > 0.9
                println("$a1 breeds with $a2")
                isa(a1, Preditor) ? push!(newanimals, Preditor(a1.breed)) : 
                                    push!(newanimals, Prey(a1.breed))
                push!(meetings, Meeting(:breed, a1.x, a1.y))
            elseif isa(a1, Preditor) && isa(a2, Prey)
                println("$a1 eats $a2")
                a2.alive = false
                a1.eaten += 1
                push!(meetings, Meeting(:kill, a2.x, a2.y))
            elseif isa(a1, Prey) && isa(a2, Preditor)
                println("$a2 eats $a1")
                a1.alive = false
                a2.eaten += 1
                push!(meetings, Meeting(:kill, a1.x, a1.y))
            end
        end
    end
#     filter!(p -> p.alive, animals)
    append!(animals, newanimals)
end

animals = Animal[Preditor() for _ in 1:4]
meetings = Meeting[]
append!(animals, [Prey() for _ in 1:40])
steps = 200
println("starting number of animals:", length(animals))
for step in 1:steps
    move!(animals)
    meet!(animals, meetings)
end
println("final number of animals:", length(animals))


starting number of animals:44
Tiger (-1.279, 1.625) eats Chicken (-1.260, 1.504)
Lion (1.398, 0.448) eats Sheep (1.308, 0.704)
Lion (1.398, 0.448) eats Goat (1.425, 0.534)
Lion (1.398, 0.448) eats Sheep (1.591, 0.333)
Chicken (0.302, -2.561) breeds with Chicken (0.065, -2.497)
Chicken (1.937, 1.615) breeds with Chicken (1.863, 1.375)
Sheep (2.427, 1.139) breeds with Sheep (2.286, 1.284)
Lion (2.001, 1.043) eats Sheep (2.257, 1.157)
Lion (2.001, 1.043) eats Chicken (2.112, 1.315)
Lion (2.001, 1.043) eats Chicken (2.036, 1.227)
Tiger (-1.990, 0.878) eats Sheep (-2.153, 0.838)
Tiger (-2.133, 0.831) eats Horse (-1.914, 0.657)
Horse (2.221, -2.270) breeds with Horse (2.015, -2.350)
Lion (-0.266, 0.681) eats Deer (-0.308, 0.387)
Horse (1.958, -2.554) breeds with Horse (2.093, -2.728)
Sheep (1.135, -1.098) breeds with Sheep (1.377, -1.214)
Tiger (-6.163, -3.633) eats Horse (-6.285, -3.736)
Deer (-6.083, -1.827) breeds with Deer (-6.277, -1.713)
Deer (-6.012, -1.947) breeds with Deer (-6.283, -1.853)
Sheep (6.712, -1.298) breeds with Sheep (6.775, -1.159)
Sheep (6.784, -1.189) breeds with Sheep (6.673, -1.256)
Eagle (-6.513, 3.234) eats Sheep (-6.638, 2.963)
final number of animals:54

In [7]:
xs = zeros(steps, length(animals))
ys = zeros(steps, length(animals))
for (i, p) in enumerate(animals)
    length(p.route) == 0 && continue
    # (steps - length(p.route) + 1)
    xs[:, i] = p.route[end][1]
    ys[:, i] = p.route[end][2]
    xs[1:length(p.route), i] = Float64[r[1] for r in p.route]
    ys[1:length(p.route), i] = Float64[r[2] for r in p.route]
end
hold(false)
plot(xs, ys, width=1000, height=800)
hold(true)
killx = Float64[m.x for m in filter(m->m.event == :kill, meetings)]
killy = Float64[m.y for m in filter(m->m.event == :kill, meetings)]
plot(killx, killy, "ok")
breedx = Float64[m.x for m in filter(m->m.event == :breed, meetings)]
breedy = Float64[m.y for m in filter(m->m.event == :breed, meetings)]
plot(breedx, breedy, "ob")


Out[7]:

Plot("Bokeh Plot" with 56 datacolumns)


In [ ]:


In [ ]: