Some useful functions/tricks in Julia’s REPL console

When writing code in Julia, I use REPL console frequently, mainly to understand how some functions or some expressions works in Julia. I usually create something like dummy matrices and vectors, and test the function/expression using that dummies. Fortunately, Julia’s REPL console provides many useful tricks for exploring how it works.

?Help

Getting help on a function in Julia is easy, just type ? followed by the name of the function to show the manual. Here’s an example

help?> eig
Base.eig(A,[irange,][vl,][vu,][permute=true,][scale=true]) -> D, V

   Computes eigenvalues and eigenvectors of "A". See "eigfact()"
   for details on the "balance" keyword argument.

      julia> eig([1.0 0.0 0.0; 0.0 3.0 0.0; 0.0 0.0 18.0])
      ([1.0,3.0,18.0],
      3x3 Array{Float64,2}:
       1.0  0.0  0.0
       0.0  1.0  0.0
       0.0  0.0  1.0)

   "eig" is a wrapper around "eigfact()", extracting all parts of
   the factorization to a tuple; where possible, using "eigfact()"
   is recommended.

Base.eig(A, B) -> D, V

   Computes generalized eigenvalues and vectors of "A" with respect
   to "B".

   "eig" is a wrapper around "eigfact()", extracting all parts of
   the factorization to a tuple; where possible, using "eigfact()"
   is recommended.

Who’s there?

Julia’s REPL provides the function whos() to display all the variables (and functions) in the environment. Take a look on this example:

julia> a = [1, 2, 3]
3-element Array{Int64,1}:
 1
 2
 3

julia> f(x) = x^2
f (generic function with 1 method)

julia> whos()
Base                          Module
Core                          Module
Main                          Module
a                             3-element Array{Int64,1}
ans                           Function
f                             Function

If we want to see all functions inside a module, we can use whos(ModuleName). Executing whos(Core) will show all functions/variables inside the Core module.

julia> whos(Core)
===                           Function
ANY                           TypeVar
ASCIIString                   DataType
AbstractArray                 DataType
Any                           DataType
Array                         DataType
Bool                          DataType
........

We can search for functions using Regex, for example if we want to see all function in Base containing the word with “inv”:

julia> whos(Base, r"inv"i)
erfcinv                       Function
erfinv                        Function
inv                           Function
invdigamma                    Function
invmod                        Function
invperm                       Function
pinv                          Function

Methods

In Julia, function is a collection of methods. Similar with the concept of polymorphism in Object Oriented Programming, in Julia, there can be many functions (methods) with the same name, but with different signature. The choice of which method to execute when we call a function is called dispatch. In Julia, the choice is based on the number of arguments passed to the function, and on the type of all arguments. If there are more than one methods satisfy the requirement, the one with the more specific types will be called. Here’s an example:

julia> f(x) = println("General method is called")
f (generic function with 1 method)

julia> f(x::Number) = println("Method for handling number")
f (generic function with 2 methods)

julia> f(x::Float64) = println("Method for handling floating point")
f (generic function with 3 methods)

julia> f(1)
Method for handling number

julia> f(1.0)
Method for handling floating point

julia> f([1, 2])
General method is called

Note that f(1.0) satisfy both f(x::Number) and f(x::Float64), but the more specific method is called.

Julia provides methods() function to show all methods in a function.

julia> methods(eye)
# 7 methods for generic function "eye":
eye{T}(::Type{Diagonal{T}},n::Int64) at linalg/diagonal.jl:95
eye(T::Type{T<:Top},m::Integer,n::Integer) at array.jl:176
eye(m::Integer,n::Integer) at array.jl:182
eye(T::Type{T<:Top},n::Integer) at array.jl:183
eye(n::Integer) at array.jl:184
eye(S::SparseMatrixCSC{Tv,Ti<:Integer}) at sparse/sparsematrix.jl:414
eye{T}(x::AbstractArray{T,2}) at array.jl:185

We can also show all methods that are applicable to a variable using methodwith() function. For example to show all methods that accept a Matrix variable:

julia> methodswith(Matrix)
67-element Array{Method,1}:
 size(a::Array{T,2}) at array.jl:20
 -(A::Array{T,2},B::Diagonal{T}) at linalg/special.jl:90
 -(A::Array{T,2},B::Bidiagonal{T}) at linalg/special.jl:90
 -(A::Array{T,2},B::Tridiagonal{T}) at linalg/special.jl:90
 -(A::Array{T,2},B::Triangular{T,S<:AbstractArray{T,2},UpLo,IsUnit}) at linalg/special.jl:90
 -(A::Array{T,2},B::SymTridiagonal{T}) at linalg/special.jl:99
..............

Which one?

In order to see which method will be chosen when we execute a function or expression, we can use @which macro.

julia> @which f(4.0)
f(x::Float64) at none:1

julia> @which 1 + 2
+(x::Int64,y::Int64) at int.jl:33

julia> @which 1.0 + 2
+(x::Number,y::Number) at promotion.jl:158

julia> @which rand(1, 2)
rand(dims::Int64...) at random.jl:123

Types-hierarchy

Since understanding Types-hierarchy is very important in Julia (it’s a key to master methods), we might want to know functions related to Type. Some basic type functions are subtypes(), super() typeof(), isa(), and issubtype(). Below are the examples:

julia> typeof(1.0)
Float64

julia> super(Float64)
FloatingPoint

julia> subtypes(Number)
2-element Array{Any,1}:
 Complex{T<:Real}
 Real

julia> subtypes(Real)
5-element Array{Any,1}:
 FixedPoint
 FloatingPoint
 Integer
 MathConst{sym}
 Rational{T<:Integer}

julia> isa(1.0, Real)
true

julia> isa(1.0, Integer)
false

julia> isa(1.0, Number)
true

julia> issubtype(Integer, FloatingPoint)
false

julia> issubtype(Integer, Number)
true

Note that some types has alias, for example Array data type which contains Vector (1-d array), Matrix (2-d array) and higher dimension Array.

julia> Vector
Array{T,1}

julia> Matrix
Array{T,2}

Compiler’s mind

Julia has built-in functions to see how the compiler parse the source code and translate it to lower representations of the code. Four macros @code_lowered, @code_typed, @code_llvm, and @code_native can be used to show four different levels of representation of the code from the highest to the lowest level.

julia> @code_lowered 1.0 + 2.0 + 3
1-element Array{Any,1}:
 :($(Expr(:lambda, {:a,:b,:c}, {{},{{:a,:Any,0},{:b,:Any,0},{:c,:Any,0}},{}}, :(begin  # operators.jl, line 82:
        return (a + b) + c
    end))))

julia> @code_lowered 3 * 2.0 + 1
1-element Array{Any,1}:
 :($(Expr(:lambda, {:x,:y}, {{},{{:x,:Any,0},{:y,:Any,0}},{}}, :(begin  # promotion.jl, line 158:
        return (top(apply))(+,promote(x,y))
    end))))

julia> @code_typed 3 * 2.0 + 1
1-element Array{Any,1}:
 :($(Expr(:lambda, {:x,:y}, {{},{{:x,Float64,0},{:y,Int64,0}},{}}, :(begin  # promotion.jl, line 158:
        return box(Float64,add_float(x::Float64,box(Float64,sitofp(Float64,y::Int64))::Float64))::Float64
    end::Float64))))

julia> @code_llvm 3 * 2.0 + 1

define double @"julia_+_3232"(double, i64) {
top:
  %2 = sitofp i64 %1 to double, !dbg !9747
  %3 = fadd double %2, %0, !dbg !9747
  ret double %3, !dbg !9747
}

julia> @code_native 3 * 2.0 + 1
        .text
Filename: promotion.jl
Source line: 158
        push    RBP
        mov     RBP, RSP
Source line: 158
        vcvtsi2sd       XMM1, XMM0, RDX
        vaddsd  XMM0, XMM1, XMM0
        pop     RBP
        ret
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s