Overview of Julia commands

A quick overview of some Julia commands and constructs.

Commands

Commands are typed at the REPL prompt or in a IJulia cell. New commands are separated by a new line or a semicolon.

Numbers, variable types

Unlike a calculator Julia has different "types" for different kinds of numbers. For example

- Integers: 2

- Rational numbers: 1//2

- Floating point numbers: 0.5

- Complex numbers 2 + 0im

As much as possible, operations involving certain types of numbers will produce output of a given type. For example, both of these divisions produce a floating point answer, even though mathematically, they need not:

2/1, 1/2
(2.0,0.5)

Some operations won't work with integer types, but will with floating point types, as the type of output can't be assured. Powers are the main example where 2^(1/2) is not defined, but 2.0^(1/2) is.

An expression like (-3.0)^(1/3) is not defined, as it can't be in general for the two types. However, Julia provides the special-case function cbrt.

Integer operations may silently overflow, producing odd answers:

2^3^4    # is 2^(3^4) = 2^81 which is too big for 64 bit integers
0

When different types of numbers are mixed, Julia will usually promote the values to a common type before the operation:

(2 + 1//2) + 0.5
3.0

Julia will first add 2 and 1//2 converting the integer 2 to rational before doing so. Then Julia will add the result, 5//2, to 0.5, promoting 5//2 to the floating point number 2.5 before proceeding.

The standard mathematical operations are implemented by +, -, *, /, ^. Parentheses are used for grouping.

Vectors

Arithmetic sequences can be defined by either

- linspace(a,b,n) which produces n values between a and b;

- a:h:b or a:b which produces values starting at a separated by h (h is 1 in the last form) until they reach b.

- general vectors can be constructed with square brackets:

[1,1,2,3,5,8]
6-element Array{Int64,1}:
 1
 1
 2
 3
 5
 8

Variables

Values can be assigned variable names, with =. There are some variants

x = 2
a_really_long_name = 3
a, b = 1, 2
a1 = a2 = 0
0

The names can be short, as above, or more verbose. They can't start with a number, but can include numbers. It can also be a fancy unicode or even an emoji.

Names may be repurposed, even with values of different types (a dynamic language), save for function names, which have some special rules.

Functions

Functions in Julia are just regular objects. There are many built-in functions and it is easy to define new functions.

We call a function by passing arguments to it, grouped by parentheses:

sin(pi)
1.2246467991473532e-16

Functions can have one or more arguments, this log function has two with the first indicating the base:

log(5, 100)   # log base 5 of 100
2.8613531161467867

Many functions can share the same generic name. For example, for base $e$ logarithms, the log function is used directly:

log(10)     # same as log(e, 10)
2.302585092994046

Julia uses the number of arguments and types of the arguments to disambiguate which method to call.

With out parentheses, the name refers to generic name and the output lists the number of available implementations.

log
log (generic function with 46 methods)

Built-in functions

Julia has numerous built-in mathematical functions.

Powers logs and roots

Besides ^, there are sqrt and cbrt for powers. In addition basic function for exponential and logarithmic functions:

sqrt(x), cbrt(x)
exp(x)
log(x) # base e
log10(x), log2(x), log(b, x)

Trigonometric functions

The $6$ standard trig functions are implemented; their implementation for degree arguments; their inverse functions; and their hyperbolic analogs.

sin, cos, tan, csc, sec, cot
sind, cosd, tand, cscd, secd, cotd
asin, acos, atan, acsc, asec, acot
sinh, cosh, tanh, csch, sech, coth
asinh, acosh, atanh, acsch, asech, acoth

Useful functions

Other useful and familiar functions are defined:

- abs(x) absolute value

- sign(x) is $\lvert x \rvert/x$ except at $x=0$, where it is $0$.

- floor(x), ceil(x) greatest integer less or least integer greater

- max(a,b) and min(a,b) larger (or smaller) of a or b

- maximum(xs) and minimum(xs) largest or smallest of the collection referred to by xs

User-defined functions

Simple mathematical functions can be defined using standard mathematical notation:

f(x) = -16x^2 + 100x + 2
f (generic function with 1 method)

The argument x is passed into the body of function.

User defined functions can have 1 or more arguments:

area(w, h) = w*h
area (generic function with 1 method)

Julia makes different methods for generic function names, so functions whose argument specification are different are different functions, even if the name is the same. This is polymorphism and means users need only remember a much smaller set of function names.

Longer functions can be defined using the function keyword, the last command executed is returned:

function f(x)
  y = x^2
  z = y - 3
  z
end
f (generic function with 1 method)

Functions without names, anonymous functions are made with the -> operator as in:

x -> cos(x)^2 - cos(2x)
(anonymous function)

These are useful when passing a function to another function.

Conditional statements

Julia provides the traditional if-else-end statements, but more conveniently has a ternary operator for the simplest case:

our_abs(x) = (x < 0) ? -x : x
our_abs (generic function with 1 method)

Looping

Iterating over a collection can be done with the traditional for loop. However, there are list comprehensions to mimic the definition of a set:

[x^2 for x in 1:10]
10-element Array{Int64,1}:
   1
   4
   9
  16
  25
  36
  49
  64
  81
 100

And map to apply a function over a collection:

map(sin, 1:4)
4-element Array{Float64,1}:
  0.841471
  0.909297
  0.14112 
 -0.756802

Plotting

Plotting is not built-in to Julia, rather added through add-on packages, for example, Julia's Gadfly package. For this package, there are three basic calling styles:

Plotting a function by passing the function object by name.

using Gadfly      # needed just once per session
plot(sin, 0, 2pi)
x -10 -8 -6 -4 -2 0 2 4 6 8 10 12 14 16 18 -8.0 -7.5 -7.0 -6.5 -6.0 -5.5 -5.0 -4.5 -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5 10.0 10.5 11.0 11.5 12.0 12.5 13.0 13.5 14.0 14.5 15.0 15.5 16.0 -10 0 10 20 -8.0 -7.5 -7.0 -6.5 -6.0 -5.5 -5.0 -4.5 -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5 10.0 10.5 11.0 11.5 12.0 12.5 13.0 13.5 14.0 14.5 15.0 15.5 16.0 f(x) -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 -3.0 -2.9 -2.8 -2.7 -2.6 -2.5 -2.4 -2.3 -2.2 -2.1 -2.0 -1.9 -1.8 -1.7 -1.6 -1.5 -1.4 -1.3 -1.2 -1.1 -1.0 -0.9 -0.8 -0.7 -0.6 -0.5 -0.4 -0.3 -0.2 -0.1 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0 -4 -2 0 2 4 -3.0 -2.8 -2.6 -2.4 -2.2 -2.0 -1.8 -1.6 -1.4 -1.2 -1.0 -0.8 -0.6 -0.4 -0.2 0.0 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 2.2 2.4 2.6 2.8 3.0

Plotting an anonymous function

plot( x -> exp(-x/pi) * sin(x), 0, 2pi)
x -10 -8 -6 -4 -2 0 2 4 6 8 10 12 14 16 18 -8.0 -7.5 -7.0 -6.5 -6.0 -5.5 -5.0 -4.5 -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5 10.0 10.5 11.0 11.5 12.0 12.5 13.0 13.5 14.0 14.5 15.0 15.5 16.0 -10 0 10 20 -8.0 -7.5 -7.0 -6.5 -6.0 -5.5 -5.0 -4.5 -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5 10.0 10.5 11.0 11.5 12.0 12.5 13.0 13.5 14.0 14.5 15.0 15.5 16.0 f(x) -1.50 -1.25 -1.00 -0.75 -0.50 -0.25 0.00 0.25 0.50 0.75 1.00 1.25 1.50 1.75 2.00 -1.25 -1.20 -1.15 -1.10 -1.05 -1.00 -0.95 -0.90 -0.85 -0.80 -0.75 -0.70 -0.65 -0.60 -0.55 -0.50 -0.45 -0.40 -0.35 -0.30 -0.25 -0.20 -0.15 -0.10 -0.05 0.00 0.05 0.10 0.15 0.20 0.25 0.30 0.35 0.40 0.45 0.50 0.55 0.60 0.65 0.70 0.75 0.80 0.85 0.90 0.95 1.00 1.05 1.10 1.15 1.20 1.25 1.30 1.35 1.40 1.45 1.50 1.55 1.60 1.65 1.70 1.75 -2 -1 0 1 2 -1.3 -1.2 -1.1 -1.0 -0.9 -0.8 -0.7 -0.6 -0.5 -0.4 -0.3 -0.2 -0.1 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8

Plotting more than one function over $[a,b]$:

plot([sin, cos, zero], 0, 2pi)
x -10 -8 -6 -4 -2 0 2 4 6 8 10 12 14 16 18 -8.0 -7.5 -7.0 -6.5 -6.0 -5.5 -5.0 -4.5 -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5 10.0 10.5 11.0 11.5 12.0 12.5 13.0 13.5 14.0 14.5 15.0 15.5 16.0 -10 0 10 20 -8.0 -7.5 -7.0 -6.5 -6.0 -5.5 -5.0 -4.5 -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5 10.0 10.5 11.0 11.5 12.0 12.5 13.0 13.5 14.0 14.5 15.0 15.5 16.0 f1 f2 f3 Color f(x) -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 -3.0 -2.9 -2.8 -2.7 -2.6 -2.5 -2.4 -2.3 -2.2 -2.1 -2.0 -1.9 -1.8 -1.7 -1.6 -1.5 -1.4 -1.3 -1.2 -1.1 -1.0 -0.9 -0.8 -0.7 -0.6 -0.5 -0.4 -0.3 -0.2 -0.1 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0 -4 -2 0 2 4 -3.0 -2.8 -2.6 -2.4 -2.2 -2.0 -1.8 -1.6 -1.4 -1.2 -1.0 -0.8 -0.6 -0.4 -0.2 0.0 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 2.2 2.4 2.6 2.8 3.0

Matrices

Matrices are created by horizontally or vertically concatenating values:

vcat(1,2,3)  # a vector Array{Int64,1}
3-element Array{Int64,1}:
 1
 2
 3
hcat(1,2,3) # a 1x3 matrix Array{Int64, 2}
1x3 Array{Int64,2}:
 1  2  3

Combining these can be useful:

hcat(vcat(1,2,3), vcat(4,5,6))
3x2 Array{Int64,2}:
 1  4
 2  5
 3  6

The above is a bit cumbersome. The [] function does both:

- vcat is done when values are separated by commas or semicolons - hcat done with values are separated by spaces (whitespace is important!)

(Not all permutations are possible)

[1 2 3; 4 5 6]  # vertically concatenate two horizontal matrices
2x3 Array{Int64,2}:
 1  2  3
 4  5  6
v = [1,2,3]; w = [4,5,6] # vertical vectors
[v w]           # horizontally concatenate
3x2 Array{Int64,2}:
 1  4
 2  5
 3  6

Blocks can be manipulated

B = [1 1; 1 1]; v= [2,2]; C=[3 3 3]
[B v; C]
3x3 Array{Int64,2}:
 1  1  2
 1  1  2
 3  3  3

Matrices can also be formed by comprehensions with two variables:

[1//(i+j+1) for i in 1:5, j in 1:5]
5x5 Array{Rational{Int64},2}:
 1//3  1//4  1//5  1//6   1//7 
 1//4  1//5  1//6  1//7   1//8 
 1//5  1//6  1//7  1//8   1//9 
 1//6  1//7  1//8  1//9   1//10
 1//7  1//8  1//9  1//10  1//11

Floating point

There are various functions to work with floating point values.

- nextfloat and prevfloat to give the floating point value to the right or left

- typemax and typemin to give the larges and smallest value representable by a type

- bits to show the bits used in storage

- Float16, Float32, Float64, BigFloat for floats of different sizes.

For example, this is the largest non-infinite floating point value for 16-bit floating point values:

prevfloat(typemax(Float16))
float16(65504.0)

The bits used for floating point numbers are detailed here

For 64-bit floating point values the first bit is the sign bit, bits 2:12 code the exponent and bits 13:64 code the significand.

For 16-bit floating point values again the first bit is the sign bit, bits 2:6 code the exponent and bits 7:16 code the significand.

Here we can see the special codings of 0, Inf and NaN:

xs =  [0, -Inf, Inf, NaN]
[rpad(i, 10) * "$(bits(x)[1]) $(bits(x)[2:6])  $(bits(x)[7:16])" for (i,x) in zip(xs, [convert(Float16, x) for  x in xs])]
4-element Array{Union(ASCIIString,UTF8String),1}:
 "0.0       0 00000  0000000000"
 "-Inf      1 11111  0000000000"
 "Inf       0 11111  0000000000"
 "NaN       0 11111  1000000000"

This shows powers of 2 and how they are coded (15 = "01111")

xs = [2.0^i for i in -2:2]
[rpad(i, 10) * "$(bits(x)[1]) $(bits(x)[2:6])  $(bits(x)[7:16])" for (i,x) in zip(xs, [convert(Float16, x) for  x in xs])]
5-element Array{Union(ASCIIString,UTF8String),1}:
 "0.25      0 01101  0000000000"
 "0.5       0 01110  0000000000"
 "1.0       0 01111  0000000000"
 "2.0       0 10000  0000000000"
 "4.0       0 10001  0000000000"

This shows the significand changing

xs = 1:8
[rpad(i, 10) * "$(bits(x)[1]) $(bits(x)[2:6])  $(bits(x)[7:16])" for (i,x) in zip(xs, [convert(Float16, x) for  x in xs])]
8-element Array{Union(ASCIIString,UTF8String),1}:
 "1         0 01111  0000000000"
 "2         0 10000  0000000000"
 "3         0 10000  1000000000"
 "4         0 10001  0000000000"
 "5         0 10001  0100000000"
 "6         0 10001  1000000000"
 "7         0 10001  1100000000"
 "8         0 10010  0000000000"

We see how the binary representation is comprised:

xs = [1 + 1//2 + 0//4 + 1//8 + 0//16 + 1//32]
[rpad(i, 10) * "$(bits(x)[1]) $(bits(x)[2:6])  $(bits(x)[7:16])" for (i,x) in zip(xs, [convert(Float16, x) for  x in xs])]
1-element Array{Union(ASCIIString,UTF8String),1}:
 "53//32    0 01111  1010100000"

Multiplying by 2 just adds 1 to the exponent

xs = [3, 6, 12, 24]
[rpad(i, 10) * "$(bits(x)[1]) $(bits(x)[2:6])  $(bits(x)[7:16])" for (i,x) in zip(xs, [convert(Float16, x) for  x in xs])]
4-element Array{Union(ASCIIString,UTF8String),1}:
 "3         0 10000  1000000000"
 "6         0 10001  1000000000"
 "12        0 10010  1000000000"
 "24        0 10011  1000000000"

Dividing by 2 subtracts 1 from the exponent

xs = [3, 3//2, 3//4, 3//8]
[rpad(i, 10) * "$(bits(x)[1]) $(bits(x)[2:6])  $(bits(x)[7:16])" for (i,x) in zip(xs, [convert(Float16, x) for  x in xs])]
4-element Array{Union(ASCIIString,UTF8String),1}:
 "3//1      0 10000  1000000000"
 "3//2      0 01111  1000000000"
 "3//4      0 01110  1000000000"
 "3//8      0 01101  1000000000"

For even faster math, some programs will work by manipulating bits.

Iterative methods

Translating pseudo code into an algorithm with Julia is usually pretty straightforward. Consider this code for the bisection method:

$$ \begin{align} &\textbf{input } a, b, M, \delta, \epsilon\\ &u \leftarrow f(a)\\ &v \leftarrow f(b)\\ &e \leftarrow b - a\\ &\textbf{if } sign(u) = sign(v) \textbf{ then stop}\\ &\textbf{for } k=1 \textbf{ to } M \textbf{ do}\\ &\quad e \leftarrow e/2 \\ &\quad c \leftarrow a + e \\ &\quad w \leftarrow f(c) \\ &\quad \textbf{if } \lvert e\rvert < \delta \textbf{ or } \lvert w \rvert < \epsilon \textbf{ then stop}\\ &\quad \textbf{if } sign(w) \neq sign(u) \textbf{ then} \\ &\quad \quad b \leftarrow c \\ &\quad \quad v \leftarrow w \\ &\quad \textbf{else} \\ &\quad \quad a \leftarrow c \\ &\quad \quad u \leftarrow w \\ &\quad \textbf{end if} \\ &\textbf{end do} \\ \end{align} $$

The bold text are commands. Here is a julia translation:

function bisection(f, a, b; M=64, delta=1e-12, epsilon=1e-12)
u,v = f(a),f(b)
e = b-a
c = Inf

if sign(u) == sign(v) error("a,b not a bracket") end
for k in 1:M
  e = e/2
  c = a + e
  w = f(c)
  if (abs(e) < delta) | (abs(w) < epsilon)
    break
  end

  if sign(w) != sign(u)
    b,v = c, w
  else
    a,u = c, w
  end
end

return c
end
bisection (generic function with 1 method)

Does it work? Let's find $\pi$:

bisection(sin, 3, 4)
3.1415926535901235

Julia uses a fairly similar set of commands with a few differences:

- input is replaced by the function keyword to begin a multi-line function

- the pseudocode $\leftarrow$ is just =, the assignment operator.

- the equality test $=$ is replaced by == (as = is assignment, not a test for equality)

- if-then-stop is replaced with an if-then-end and stop is handled by error in one case and break in another, as that is what the logic dictates.

- or becomes | (and and becomes &). (There are also shortcut versions && and ||. These are often used used for control flow in julia, as with sign(u) == sign(v) && error("...").)

- for-to-do ... end do becomes a for loop with the syntax for var in collection ... end

- if-else-end if becomes if-else-end.

A more subtle point is the value of c in the pseudo code is assigned within a for loop. Our "mental" compiler has no trouble recognizing this assignment in producing the answer. However, in real Julia code this assignment will not be visible outside the block, as a block-local variable is created unless there is a global variable for assignment. In the julia code this is done by initializing c to Inf. As well, we explicitly return c from our function as that is the approximate answer.