Julia: The best of two worlds

Overview

Teaching: 45 min
Exercises: 15 min
Questions
  • What is Julia, and why should I learn another language?

Objectives
  • Learn the basics of Julia and its use in scientific computing

The Julia programming language fills the gap between the world of compiled languages like C/C++ and Fortran and interpreted languages like Python and R.

Julia is a flexible dynamic language, appropriate for scientific and numerical computing but with performance comparable to traditional statically-typed languages.

Being a very young language it is not as well known as the other languages that have being around for a long time.

Introduction to Julia Programming

As we have done for the other languages, we offer here a very brief introduction to the 5 basic elements to know about in every programming languages. Variables, Control Flow, Input/Output, Packages and Debugging/Profiling/Benchmarking.

Variables

Variables are declared as in most interpreted languages, just use a good name and set the values

x = 3.14
n = 10

Julia is case sensitive for variables and accept both variables and strings to be UTF8

salut = 안녕하세요"
\alpha<tab>=1.0

Julia offers primitive numeric types for both integers and floating-point numbers (truncated real numbers). The following table describes them

Integer variables

Type Signed Number of bits Smallest value Largest value
Int8 yes 8 -2^7 2^7 - 1
UInt8 no 8 0 2^8 - 1
Int16 yes 16 -2^15 2^15 - 1
UInt16 no 16 0 2^16 - 1
Int32 yes 32 -2^31 2^31 - 1
UInt32 no 32 0 2^32 - 1
Int64 yes 64 -2^63 2^63 - 1
UInt64 no 64 0 2^64 - 1
Int128 yes 128 -2^127 2^127 - 1
UInt128 no 128 0 2^128 - 1
Bool   8 false (0) true (1)

Floating-point variables

Type Precision Number of bits
Float16 half 16
Float32 single 32
Float64 double 64

Julia offers a middle position with variables, you do not need to be too concern about them but if you offer information about the variables, Julia can take that information and give you performance advantages out of it.

You can get access to the type of a variable at a given time. Contrary to what you can think from the mathematical point of view the integer 1 is not the same as the floating point 1.0. Most machines nowadays are 64 bits but still you can get information about the Word size of the machine where you are running with Sys.WORD_SIZE

julia> a=1
1

julia> typeof(a)
Int64

julia> a=1.0
1.0

julia> typeof(a)
Float64

julia> Sys.WORD_SIZE
64

One of the big differences between python 2 and 3 is dividing integers. For an expression such as 2/3 python 2 returns 0, python 3 returns 0.6666666666. Julia works like python 3, promoting both variables into floating point numbers and returning another floating.

For floating point numbers, you can create Float32 or Float64 by adding a f or e as we can see below:

julia> a=3.14f0
3.14f0

julia> typeof(a)
Float32

julia> b=3.14e0
3.14

julia> typeof(b)
Float64

A machine can only represent truncated real numbers. The distance between 1 and the closest number that the machine can represent that is larger than 1 is called the machine epsilon. This example shows how get the epsilon and why knowing about it can be important

julia> eps()
2.220446049250313e-16

julia> eps()/2
1.1102230246251565e-16

julia> 1+(eps()/2)==1
true

The truncated representation of real numbers results in gaps from one number to the next real number representable as different, the functions prevfloat and nextfloat can give you exactly what is the next real number.

julia> prevfloat(1.0)
0.9999999999999999

julia> nextfloat(1.0)
1.0000000000000002  

If you are working with numbers bigger than those representable with the primitives of the language, the GNU Multiple Precision Arithmetic Library (GMP) and the GNU MPFR Library were your alternatives to work with them (some applications in Statistical Mechanics rely on those ultra high precision numerics). However, working with GMP and MPFR have been always a challenge as even the simplest operations require many lines of C code. Julia wrap that complexity inside de BigInt and BigFloat datatypes and allow you to go to huge numbers for the price of much lower performance.

julia> typemax(Int64)
9223372036854775807

julia> typemax(Int64)+1
-9223372036854775808

julia> BigInt(typemax(Int64))+1
9223372036854775808

julia> BigInt(typemax(Int64))+BigInt(typemax(Int128))
170141183460469231740910675752738881534

julia> BigInt(typemax(Int64))*BigInt(typemax(Int128))
1569275433846670190788806172341447372284678185363

Julia allow to express equations in a way that is very close to the way math is written in paper. Something quite unusual in any other programming language. See for example the polynomials

julia> x=2
2

julia> 6x
12

julia> 6x^2
24

julia> (1+6x)^2
169

julia> (6x)^2+2(6x)+1
169

julia> (6x)*(6x+2)+1
169

Mathematical operations are very similar to other interpreted languages the next block explore a few of them:

julia> x=2
2

julia> y=3
3

julia> x+y
5

julia> x%y
2

julia> y%2
1

julia> y%x
1

julia> x^y
8

julia> x+=y
5

julia> x
5

julia> y
3

Complex and Rational Numbers

Complex numbers are created by adding the suffix im to the imaginary partition

julia> sqrt(2)
1.4142135623730951

julia> sqrt(2im)
1.0 + 1.0im

julia> sqrt(complex(-1))
0.0 + 1.0im

julia> sqrt(-1)
ERROR: DomainError:
sqrt will only return a complex result if called with a complex argument. Try sqrt(complex(x)).
Stacktrace:
 [1] sqrt(::Int64) at ./math.jl:434

Rational numbers are created using the // operator

julia> 1//3
1//3

julia> (1//3)^3
1//27

julia> 3(1//3)
1//1

julia> 3(1/3)
1.0

julia> 1/3
0.3333333333333333

julia> 2(1//3)
2//3

julia> 2(1/3)
0.6666666666666666

Key Points

  • Julia offers the advantages of interpreted languages like R or Python and good performance, approaching that of statically-compiled languages like C