Scientific Computing, TU Berlin, WS 2019/2020, Lecture 03
Jürgen Fuhrmann, WIAS Berlin
typeof()
function sometypes()
i::Int8=10
@show i,typeof(i)
x::Float16=5.0
@show x,typeof(x)
z::Complex{Float32}=15+3im
@show z,typeof(z)
return z
end
z1=sometypes()
@show z1,typeof(z1);
Vectors and Matrices have concrete types as well:
function sometypesv()
iv=zeros(Int8, 10)
@show iv,typeof(iv)
xv=[Float16(sin(x)) for x in 0:0.1:1]
@show xv,typeof(xv)
return xv
end
x1=sometypesv()
@show x1,typeof(x1);
Structs allow to define user defined concrete types
struct Color64
r::Float64
g::Float64
b::Float64
end
c=Color64(0.5,0.5,0.1)
@show c,typeof(c);
Types can be parametrized (similar to array)
struct TColor{T}
r::T
g::T
b::T
end
c=TColor{Float16}(0.5,0.5,0.1)
@show c,typeof(c);
f
can be listed calling methods(f)
function test_dispatch(x::Float64)
println("dispatch: Float64, x=$(x)")
end
function test_dispatch(i::Int64)
println("dispatch: Int64, i=$(i)")
end
test_dispatch(1.0)
test_dispatch(10)
methods(test_dispatch)
using LinearAlgebra
methods(det)
The function/method concept somehow corresponds to C++14 generic lambdas
auto myfunc=[](auto &y, auto &y)
{
y=sin(x);
};
is equivalent to
function myfunc!(y,x)
y=sin(x)
end
Many generic programming approaches possible in C++ also work in Julia,
If not specified otherwise via parameter types, Julia functions are generic: "automatic auto"
Examples of abstract types
function sometypesa()
i::Integer=10
@show i,typeof(i)
x::Real=5.0
@show x,typeof(x)
z::Any=15+3im
@show z,typeof(z)
return z
end
sometypesa()
Though we try to force the variables to have an abstract type, they end up with having a conrete type which is compatible with the abstract type
supertype(Float64)
using InteractiveUtils
subtypes(AbstractFloat)
subtypes(Float64)
supertype(Any)
Walking the the type tree
function showtypetree(T, level=0)
println(" " ^ level, T)
for t in subtypes(T)
showtypetree(t, level+1)
end
end
showtypetree(Number)
We can have a nicer walk through the type tree
by implementing an interface method AbstractTrees.children
for types:
using Pkg
Pkg.add("AbstractTrees")
using AbstractTrees
AbstractTrees.children(x::Type) = subtypes(x)
AbstractTrees.print_tree(Number)
Abstract types are used to dispatch between methods as well
function test_dispatch(x::AbstractFloat)
println("dispatch: $(typeof(x)) <:AbstractFloat, x=$(x)")
end
function test_dispatch(i::Integer)
println("dispatch: $(typeof(i)) <:Integer, i=$(i)")
end
test_dispatch(one(Float16))
test_dispatch(10)
methods(test_dispatch)
Now, depending on the input type for test_dispatch
, a generic or a specific method is called
Testing of type relationships
@show Float64<: AbstractFloat
@show Float64<: Integer
@show Int16<: AbstractFloat;
From [Introduction to Writing High Performance Julia](https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxibG9uem9uaWNzfGd4OjMwZjI2YTYzNDNmY2UzMmE) by D. Robinson
Define a function
g(x)=x+x
@show g(2)
methods(g)
Parse into abstract syntax tree
@code_lowered g(2)
println("-------------------------------------")
@code_lowered g(2.0)
Type inference according to input
@code_warntype g(2)
println("-------------------------------------")
@code_warntype g(2.0)
LLVM Bytecode
@code_llvm g(2)
println("-------------------------------------")
@code_llvm g(2.0)
Native assembler code
@code_native g(2)
println("-------------------------------------")
@code_native g(2.0)
Macros for performance testing:
function ftest(v::AbstractVector)
result=0
for i=1:length(v)
result=result+v[i]^2
end
return result
end
@time ftest(ones(Float64,100000))
@time ftest(ones(Float64,100000))
Run for a different type
@time ftest(ones(Int64,100000))
@time ftest(ones(Int64,100000))
As an exception, for this example we use the CPUTime package which works without macro expansion.
Pkg.add("CPUTime")
using CPUTime
Declare a long vector
myvec=ones(Float64,1000000);
Sum up its values
CPUtic()
begin
x=0.0
for i=1:length(myvec)
global x
x=x+myvec[i]
end
@show x
end
CPUtoc();
Alternatively, put the sum into a function
function mysum(v)
x=0.0
for i=1:length(v)
x=x+v[i]
end
return x
end
Run again
CPUtic()
begin
@show mysum(myvec)
end
CPUtoc();
Julia Gotcha #1: The REPL (terminal) is the Global Scope.
Use @benchmark for testing small functions
Pkg.add("BenchmarkTools")
using BenchmarkTools
function g()
x=1
for i = 1:10
x = x/2
end
return x
end
@benchmark g()
function h()
x=1.0
for i = 1:10
x = x/2
end
return x
end
@benchmark h()
@code_native g()
@code_native h()
Once again, "boxing" occurs to handle x: in g()
it changes its type from Int64 to Float64:
@code_warntype g()
@code_warntype h()
So, when in doubt, explicitely declare types of variables
module TestModule
function mtest(x)
println("mtest: x=$(x)")
end
export mtest
end
TestModule.mtest(13)
using .TestModule
mtest(23)
LOAD_PATH
LOAD_PATH
by adding e.g. the actual directory
push!(LOAD_PATH, pwd())
Do this e.g. in the startup file .julia/config/startup.jl
"
in them with """
...)
open("TestModule1.jl", "w") do io
write(io, """
module TestModule1
function mtest1(x)
println("mtest1: x=",x)
end
export mtest1
end
""")
end
using TestModule1
TestModule1.mtest1(23)
using TestModule1
mtest1(23)
Package
with a subdirectory src
Package/src/Package.jl
defines a module named Package
open("myfile.jl", "w") do io
write(io, """myfiletest(x)=println("myfiletest: x=",x)""")
end
include("myfile.jl")
myfiletest(23)
module MyHomework
function main(;optional_parameter)
println("Hello World")
end
end
This notebook was generated using Literate.jl.