If you opt for running your ML project code locally on your machine, one of the very first things to do is to take care of the ML environment setup. But why and how?
After having introduced in one of my previous posts optimization and linear programming, I explain the basics of the Julia programming language in this article. The article represents a tutorial and is based on the official Julia documentation. My tutorial covers the key aspects that I will later use, in upcoming blog posts, for solving supply chain and operation research problems (e.g. network problems). My tutorial also highlights some major syntactic and functional differences when compared to other popular programming languages (namely Python and Matlab).
Defining variables in Julia
A variable is a name associated with a value and saved in the computer memory. Assigning a value for the variable is done using the = operator. Unicode can be used as a variable name. Most Julia editors support LaTeX syntax which can be used to create Unicode characters. The double quotes ” are used for strings while the single quote ‘ is used for a character.
Here are some examples demonstrating the above explanation.
In[]:
x = 7 # variable name: x; variable value = 7
Out[]:
7
In[]:
y = 10 # variable name: y; variable value = 10
Out[]:
10
Variable names are case-sensitive and have no semantic meaning.
In[]:
Y = 100
Out[]:
100
In[]:
n = "Jaafar" # variable name: n; variable value = "Jaafar" which is a string
Out[]:
"Jaafar"
In[]:
Letter = 'a' # character
Out[]:
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
In[]:
α = 1 # unicode is easier in Julia: write \alpha and press tab
Out[]:
1
In[]:
😠=0 # \:angry: and then press <tab>
Out[]:
0
Integer and floating point numbers
Integers and floating points are the building blocks of mathematical operations. Using the typeofcommand in Julia I can find the type of any pre-defined variable.
In[]:
x = 1; #semicolon is to avoid printing the variables
y = 10000;
z = 1.1;
d = 5e-3
println("x is ", typeof(x))
println("y is ", typeof(y))
println("z is ", typeof(z))
println("d is ", typeof(d))
Out[]:
x is Int64
y is Int64
z is Float64
d is Float64
Using the Sys.WORD_SIZE, Julia’s internal variable, I can indicate whether the targetted system is 32-bit or 64-bit.
In[]:
# 64-bit system
Sys.WORD_SIZE
Out[]:
64
Main mathematical operations in Julia
The arithmetic operations are similar to Python except for the power. For power operations, Julia uses ^ instead of ** (as known from Python).
Boolean operations are as follows:
a && b: a and b
a || b: a or b
!a: negation
In[]:
a = 1;
b = 3;
# Addition
a + b;
# Subtraction
a - b;
# times
a*b;
# divison
a/b;
# Power
a^b; # this is different from python where ** is used to raise a to the bth power
#Updating operators
a+=1; # a = a + 1
b*=2; # b = b * 2
Vectorized operators are very important in linear algebra. Dot operators are used for arrays where elementary operations are performed.
NamedTuples: Exactly like tuples but also assign a name for each variable
Dictionaries: Unordered mutable collections of pairs: key-value
In[]:
# Tuple
favoritesongs = ("outnumbered", "Power Over Me", "Bad Habits") # elements have the same type
# Tuple
favoritethings= ("yellow", 'j', pi) # elements with different types
# NamedTuple
favoritesongs_named = (a = "outnumbered", b = "Power Over Me", c = "Bad Habits") # it is between a Tuple and Dictionary
# Dictionary
myDict = Dict("name" => "Jaafar", "age" => "twenty", "hobby"=> "biking")
Out[]:
Dict{String, String} with 3 entries:
"name" => "Jaafar"
"hobby" => "biking"
"age" => "twenty"
In[]:
# Tuple access
favoritesongs[1] # indexing starts by 1 not 0 (unlike Python)
# NamedTuple access
favoritesongs_named[1] # accessed by index
favoritesongs_named.a # accessed by key
# Dictionary access
myDict["name"] # call the kay to output the value in the dictionary
Out[]:
"Jaafar"
Vectors, arrays, and matrices
Like any numerical computation language, Julia provides an easy way to handle matrices and their corresponding operations. Unlike Matlab, Julia arrays are indexed with square brackets, A[i,j]. However, similarly to Matlab, indexing starts using one, not zero, which makes it more convenient especially in loops later by using f(i) instead of f(i-1).
array: ordered and mutable collection of items of the same type
vector: array of dimension one
matrix: array of dimension two
tensor: array of n-dimension (usually 3 and above)
In[]:
#vector
array_V = [1, 2, 3, 4, 5, 6, 7, 8, 9] # acts as a vector of one-dimension
typeof(array_V)
Out[]:
Vector{Int64} (alias for Array{Int64, 1})
It is important to note that ranges act like vector. However, specifying a range is easier to code. The syntax is as follows: range_name = start:step:end
In[]:
# Range
r = 1:1:9
Out[]:
1:1:9
In[]:
collect(r) # transofrm the range output to a vector output (better output)
Out[]:
9-element Vector{Int64}:
1
2
3
4
5
6
7
8
9
More on indices and ranges in Julia
Working with matrices and arrays, in general, requires good command of indexing and slicing operations in Julia. This can be done more easily using the ranges. This is due to their compact code.
In[]:
# Define an array
name_letters = ['j','a','a','f','a','r']
# Index the array
name_letters[1] # returns j
# slice the array using a range: start:end
name_letters[1:3] # returns jaa
# slice the array using a range with step of 2: start:step:end
name_letters[1:3:6] # returns jf
Out[]:
2-element Vector{Char}:
'j': ASCII/Unicode U+006A (category Ll: Letter, lowercase)
'f': ASCII/Unicode U+0066 (category Ll: Letter, lowercase)
Printing output in Julia
Although printing the output of the code is simple it is important for e.g. debugging the code. Print commands are also helpful to readers that are trying to understand the function and purpose of the code.
In[]:
# Print on new line
println("Jaafar") ;
println("Ballout");
# Print one same line
print("Jaafar");
print(" Ballout");
Out[]:
Jaafar
Ballout
Jaafar Ballout
Specifying loops and conditions in Julia
Both loops and conditions in Julia require an end command, unlike Python where indentation is enough to end the if-condition, loop, or function-definition.
In[]:
#If condition
if length("Jaafar") > length("Ballout")
print("Your first name is bigger than your last name")
elseif length("Jaafar") == length("Ballout")
println("First name and last name have same number of characters")
else
println("Your first name is smaller than your last name")
end
Out[]:
Your first name is smaller than your last name
The code block below prints each character in my name into a new line.
In[]:
name_letters = ['j','a','a','f','a','r']
# For loop
for i in 1:length(name_letters)
println(name_letters[i])
end
Out[]:
j
a
a
f
a
r
The code below finds the location or index of a character in my name.
In[]:
#If condition in For Loop
for i in 1:length(name_letters)
if name_letters[i] == 'a'
println(i)
end
end
Out[]:
2
3
5
Defining functions in Julia
Functions, e.g. routines or methods, can be defined in Julia. The linear optimization model, below, is from my previous blog post. In the coding example that follows, I define the objective function as a function in Julia.
Here is how I can define the objective function in Julia:
In[]:
function max(x,y)
return 5x + 4y # no need to write the multiplication * sign; Julia will understand
end
Out[]:
max (generic function with 1 method)
In[]:
# Optimal soultion of the constrained problem above from previous post
z = max(3.75,1.25) # optimal value
print(z)
Out[]:
23.75
Import files into Julia
Importing files is very import especially in supply chain management and logistics problems. Because I might use .csv files in future posts, explaining how to import these files in Julia is necessary at this stage. I will be using Pandas.jl which is a Julia interface to the excellent Pandas package in Python.
I will introduce useful packages like DataFrames.jl and PyPlots in Julia in future work. These packages are very useful for solving network problems as known from supply chain management and operations research.