The Living Thing / Notebooks :

Julia

The hippest way to get your IEEE754 on

Julia: A JIT-compiled language that aims for high performance scientific computation.

Why

Some of Julia’s community makes ambitious claims about julia being the fastest and bestest thing ever.

Unsurprisingly julia is no panacea for all programming ills, nor will it be when the warts have been sanded off. It is pretty well designed for numerical computation. In my (non-rigorous) experiments it seems to do better than the jit compilation in, e.g. cython if you have dynamically defined code (e.g you are doing a monte carlo simulation, but don’t know the density ahead of time). OTOH it uses lots of memory, so don’t be running this on your raspberry pi. The community process is problematic, (see also) However, the debugging options are not impressive, and library support is patchy, especially at this early stage.

No magic bullet, but a handy arrow for the quiver.

That said, the idea of a science-users-first JIT language is timely, and Julia is that. Python has clunky legacy issues in the numeric code and a patchy API. Matlab is expensive and nasty for non-numerics. Lua has some good science libraries and could likely have filled this niche but for AFAICT sociological reasons has not acquired the hipness or critical mass of Julia.

And there are some things specific to Julia which are serious selling points, aside from the language-feature one-upmanship.

The language

Intros

In order of increasing depth

A Mental Model for Julia: Talking to a Scientist

See also his 7 Julia gotchas

Philosophy

What are Symbols?

Implementing methods for custom types is clunky

You want to implement a standard interface on your type so you can, e.g. iterate over it.

julia> function Base.getindex(S::Squares, i::Int)
            1 <= i <= S.count || throw(BoundsError(S, i))
            return i*i
        end

julia> Squares(100)[23]
529

If you are lucky you might be able to inherit from AbstractArray. However, this will require injecting your methods into another namespace; in this case, Base:

julia> struct SquaresVector <: AbstractArray{Int, 1}
            count::Int
        end
julia> Base.size(S::SquaresVector) = (S.count,)
julia> Base.IndexStyle(::Type{<:SquaresVector}) = IndexLinear()
julia> Base.getindex(S::SquaresVector, i::Int) = i*i

The type system is reasonably logical, it’s just not obvious if you are used to classical OOP.

It allocates for and copies array slices per default

Consider using views for slices, they say, which means not using slice notation but rather the view function, or the handy @views macro.

Custom types and containers are scruffy

If you want fast numerics but you are not sure of your float precision, you should not use AbstractFloat for your argument type definitions (although it works fine for simple types). This is unfortunate, since there are good reasons to mix 32bit and 64 bit floats without casting - e.g. GPUs - and writing the same identical code twice seems awkward.

The idiomatic way to do this is using parametric types and parametric methods and so-called orthogonal design.

The documentation isn’t clear on this, due to being mostly about structs rather than arrays, but AFAICT this parametric declaration is the goods if you want your method to work with an array of Float32, or of Float64, or of Float16, or of Decimal, but not an array which mixes those types. (Probably what you want in most plausible real-world use-cases, and what computers are good at vectorizing.)

my_mult(
    x::AbstractArray{T},
    y::AbstractArray{U},
)  where {T<:Real,U<:Real}
  return x .* y
end

There is a further wrinkle, though. See above re: array copies. It turns out that array Views are not in fact Arrays. Also, sparse arrays are not actually Arrays. What we actually want is probably are array types like the following. (It’s simpler for arbitrary dimenion arrays, since there is AFAICT no support for sparse types in dimension greater than 3)

RealMatrixType = Union{AbstractMatrix{F},SparseMatrixCSC{F}} where {F<:Real}
RealVectorType = Union{AbstractVector{F},SparseVector{F}} where {F<:Real}

It’s unstable and hangs all the time

Yep.

Especially when it’s doing things that are supposed to be julia specialities, such as JIT-compiling dynamic inner functions. Use the recommended julia command:

killall -9 julia

FWIW this problem has mostly occurred for me using the JunoPro Intel MKL builds. Vanilla Juno is much more stable.

Workflow sucks

Yes.

You are using Julia because it is dynamic and because it is fast, but if you try to use code in a dynamic fashion, in the global namespace, it is no longer fast. Many complicated interactions ensue with the module system, and the recommended workarounds keep changing.

Julia>=0.6 workflow tips, summarised for you:

Put code under development in a temporary module. Create a file, say Tmp.jl, and include within it

module Tmp
  <your definitions here>
end

Create another file, say tst.jl, which begins with import Tmp and includes tests for the contents of Tmp. reflect updates by using reload("Tmp").

reload("Tmp")
include("tst.jl")

What works more easily for me is running, in Juno or jupyter console:

@include("tmp.jl")

Argument syntax is only OK

Keyword arguments exist but do not participate in method dispatch. Basically, keyword arguments are second-class citizens and might make things slow or stupid if you need to specialise your code based on them. So don’t use arguments like that.

Going fast isn’t necessarily magical

You still need to optimize and hint.

Does this help?

Unsubstantiated rumour is that the following optimises system julia installation for local CPU:

include(
    joinpath(
        dirname(JULIA_HOME), "share", "julia", "build_sysimg.jl"
    )
)
build_sysimg(force=true)

IDEs/workbooks

There is a reasonable IDE called juno, built on atom. There is jupyter integration through IJulia.

Both these have their own annoyances. e.g. Juno is single-window only so you can’t use multiple monitors, and thus you end up squinting at tiny windows of code hidden between all the outputs. Also, Atom’s panes just aren’t well-designed for this use-case so it’s full of a million tiny frictions.

If you install Juno as an app, but you also already use Jupyter, there is an additional annoyance because it hijacks your atom install in a confusing way and mangles your various package preferences. I recommend installing it from within atom via the uber-juno package.

Possibly you can bypass this using homebrew? I didn’t try. But maybe give this a burl:

brew cask install juno

FWIW, I don’t like juno, and never use it. IJulia and visual studio code together have a less annoying, zippier and more stable workflow IMO.

IJulia also does its own overzealous installs per default, profligately installing another copy of jupyter, which you then have to update etc separately. Boring. You can bypass this by commanding it to use the perfectly good jupyter you already have installed:

ENV["JUPYTER"] = "/usr/local/bin/jupyter"
Pkg.add("IJulia")

Now IJulia appears as a normal kernel in your normal jupyter setup.

There is a package called Weave.jl which is inspired by R‘s knitr but compatible with jupyter, which could probably be used to fashion a working academic paper if you used the right pandoc hacks.

Pkg.add("Weave")

API, FFIs

See the API list

Calling C from Julia

Sort of easy, but there is a tedious need to define the call signature at call time.

R

XRJulia:

This package provides an interface from R to Julia, based on the XR structure, as implemented in the XR package, in this repository.

Rjulia:

rjulia provides an interface between R and Julia. It allows a user to run a script in Julia from R, and maps objects between the two languages.

Python

PyCall.js invokes python.

Toolkits

Data loading/saving/exchange

The names are nearly all self explaining

Debugging and coding

Debugger, Gallium.jl:

This is the julia debugger. Please note that the 0.6 version of this package currently does not support breakpointing, C/C++ debugging or native code inspection. These features are being rebuilt, but were never particularly reliable in prior versions of this package and a cause of instability for the more mature features. In exchange, this package features a significantly more robust pure julia debug prompt, provided by ASTInterpreter2. Please file interpreter issues against that package.

Linter, Lint.jl (also has an atom linter plugin):

Signal processing

Numerical tours of signal processing in Julia

JuliaAudio processes audio.

Images.jl processes images.

Statistics

DataFrames are provided by DataFrames.jl, and also DataTables.jl. The two are subtly incompatible in completely boring ways which you can hopefully ignore soon. For now, use IterableTables.jl to translate where needed. Blech. One can access dataframes (And DataTables and SQL databases and streaming data sources) using Query.jl. You can load a lot of the r standard datasets using RDatasets

using RDatasets
iris = dataset("datasets", "iris")
neuro = dataset("boot", "neuro")

Another notable product of JuliaStats organisation is Distributions.jl, a probability distribution toolkit providing densities, sampling etc.

Turing.jl does posterior inference.

Turing.jl is a Julia library for (universal) probabilistic programming. Current features include:

Possibly it is a competitor of Klara.jl, the Juliastats MCMC.

Plotting

The julia wiki includess worked examples of various engines.

Gadfly

The aspirational ggplot clone is Gadfly. It’s elegant, well integratd into the statistics dataframe ecosystem, but missing some stuff, and has some weird gotchas.

One of them is that the plots it produces by default are staggeringly gigantic. They will fill your hard drive and your github repositories with gigabytes of interactive gunk that you may never notice exists and probably don’t want to use, and definitely don’t want to keep. You avoid this by not just plotting, by plotting to, e.g., PNG:

First you will need to install the Fontconfig and Cairo bonus libraries

Pkg.add("Gadfly")
Pkg.add("Cairo")
Pkg.add("Fontconfig")

Now you can force PNG output:

draw(
  PNG(150, 150),
  plot(data,  x=:gamma, y=:p, Geom.point, Geom.line)
)

For me this results in a 40-fold savings factor in storage, and stops my github account from going instantly over quota.

Gadfly is based on a clever declarative vector graphics system called Compose.jl, which is independently useful.

Plots.jl

Plots.jl wraps many other plotting packages as backends, although notably not Gadfly. The author explains this as being because Gadfly does not support 3d or interactivity, although since I want neither of those things in general, especially for publication this is a a contraindication for the compatibilities of our mutual priorities. I have enough grief trying to persuade my department that this internet thing is anything other than a PDF download engine.

Plots is especially convenient for its fast plots of big things. This seems to be because it wraps GR.jl which in turn wraps GR, a cross-platform visualisation framework:

GR is a universal framework for cross-platform visualization applications. It offers developers a compact, portable and consistent graphics library for their programs. Applications range from publication quality 2D graphs to the representation of complex 3D scenes.[…]

GR is essentially based on an implementation of a Graphical Kernel System (GKS) and OpenGL. […] GR is characterized by its high interoperability and can be used with modern web technologies and mobile devices. The GR framework is especially suitable for real-time environments.

Vega

Vega.jl binds to a javascript ggplot-alike, vega. There is a “higher level” binding also called VegaLite.jl.

It’s unclear to me how either of these work with print graphics, and they are both lagging behind the latest version of the underlying Vega library so I’m nervous of them.

Others

Winston.jl has a brutalist simple approach but seems to just go.

Differentiating, optimisation

Laplacians.jl by Dan Spielman et al is an advanced matrix factorisation toolkit.

The deep learning toolkits are, for the moment, lacking features. Perhaps they’ll get there?

tensorflow.jl. (Surely one misses the benefit of Julia by using tensorflow, since there are two different array-processing infrastructures to pass between, and a very different approach to JIT versus pre-compiled execution. Or is it a match made in heaven?)

Flux.jl sounds like a reimplementation of tensorflow-style differentiable programming inside Julia, which strikes me as the way you’d actually do this right, given the end-to-end-optimised design philosophy of julia.

Flux is a library for machine learning. It comes “batteries-included” with many useful tools built in, but also lets you use the full power of the Julia language where you need it. The whole stack is implemented in clean Julia code (right down to the GPU kernels) and any part can be tweaked to your liking.

However, it’s missing many features of tensorflow.

The juliadiff project produces ForwardDiff.jl and ReverseDiff.jl which do what you would expect, namely autodiff.

Approximating

ApproxFun.jl does Chebychev and Fourier interpolations.

UIs and servers

HttpServer does basic http protocol serving; this is made modular and composable by Mux.jl. Fancy caching and templating etc come from Genie.jl.

Escher.jl goes further, rendering HTML UI widgets etc.

Various other options are listed in aviks’ stackoverflow answer.