Arithmetic on `isbits` type parameters - julia

I want a type-stable version of the following (this isn't):
foo(::Val{k}) where k = Val{k+1}()
Is this possible?

Does this count?
#generated bar(::Val{k}) where k = :(Val{$(k+1)}())
For example:
julia> bar(Val{2}())
Val{3}()
julia> #code_warntype bar(Val{2}())
Variables:
#self# <optimized out>
#unused# <optimized out>
Body:
begin # line 1:
return $(QuoteNode(Val{3}()))
end::Val{3}

Related

How to pass a function typesafe in julia

Let's say, I want to pass a function to another function:
function foo()
return 0;
end
function bar(func)
return func();
end
print(bar(foo));
But you can make functions typesafe:
function func(t::Int)
print(t);
end
func(0); #produces no error
func("Hello world"); #produces an error
I didn't found out, how I combine both, that means, how can I explicitly define a parameter of bar, like func, to be a function, possibly with certain input / output argument types.
Thanks in advance for any help.
If I understand you correctly you want to make sure the passed function returns a specific type? The simplest thing is to just typeassert the return value at runtime:
julia> function f(func)
val = func()::Int # Error if the return value is not of type Int
return val
end
f (generic function with 1 method)
julia> f(() -> 1)
1
julia> f(() -> 1.0)
ERROR: TypeError: in typeassert, expected Int64, got Float64
Stacktrace:
[1] f(::var"#7#8") at ./REPL[5]:2
[2] top-level scope at REPL[8]:1
Alternatively you can use the FunctionWrappers.jl package (which will convert to the specified return type, or error if the conversion is not possible):
julia> using FunctionWrappers: FunctionWrapper
julia> function f(func::FunctionWrapper{Int,<:Tuple})
val = func()
return val
end;
julia> function f(func)
fw = FunctionWrapper{Int,Tuple{}}(func)
return f(fw)
end;
julia> f(() -> 1)
1
julia> f(() -> 1.0) # Can convert to Int
1
julia> f(() -> 1.2) # Can not convert to Int
ERROR: InexactError: Int64(1.2)
A function is of type Function. You can easily check this:
julia> foo() = 1;
julia> T = typeof(foo)
typeof(foo)
julia> supertype(T)
Function
julia> foo isa Function
true
This will not necessarily cover all callable types, as you can make any type callable:
julia> struct Callable end
julia> (::Callable)(x::Number) = x + one(x)
julia> callable = Callable()
Callable()
julia> callable(5)
6
julia> callable isa Function
false

Best practice for improving type-stability of `hvcat`?

Please take a look at the following MWE:
julia> function foo()
# do something
R = rand(3,3)
t = rand(3)
y = [ R t;
0 0 0 1]
# do something
end
foo (generic function with 1 method)
julia> #code_warntype foo()
Variables:
#self#::#foo
R::Array{Float64,2}
t::Array{Float64,1}
Body:
begin
# ..........................
R::Array{Float64,2} = $(Expr(:invoke, MethodInstance for rand!(::MersenneT
wister, ::Array{Float64,2}, ::Int64, ::Type{Base.Random.CloseOpen}), :(Base.Rand
om.rand!), :(Base.Random.GLOBAL_RNG), SSAValue(4), :((Base.arraylen)(SSAValue(4)
)::Int64), :(Base.Random.CloseOpen))) # line 3:
# .............................
t::Array{Float64,1} = $(Expr(:invoke, MethodInstance for rand!(::MersenneT
wister, ::Array{Float64,1}, ::Int64, ::Type{Base.Random.CloseOpen}), :(Base.Rand
om.rand!), :(Base.Random.GLOBAL_RNG), SSAValue(8), :((Base.arraylen)(SSAValue(8)
)::Int64), :(Base.Random.CloseOpen))) # line 4:
return $(Expr(:invoke, MethodInstance for hvcat(::Tuple{Int64,Int64}, ::Ar
ray{Float64,2}, ::Vararg{Any,N} where N), :(Main.hvcat), (2, 4), :(R), :(t), 0,
0, 0, 1))
end::Any
It shows that hvcat can't infer the type because it needs to know the size of R and t which is not compile time info. My current workaround is to write something like this:
y = zeros(4,4)
y[1:3,1:3] = R
y[:,4] = [t; 1]
But it looks a little bit cumbersome, is there any concise way to do this?
Replacing the scalar parameters with Array typed ones seems to do the trick:
function foo()
# do something
R = rand(3,3)
t = rand(3)
y = [ R t;
[0 0 0 1]] # <-- the change is here
# do something
end
The #code_warntype is:
julia> #code_warntype foo()
Variables:
#self# <optimized out>
R::Array{Float64,2}
t::Array{Float64,1}
y <optimized out>
Body:
begin
⋮
⋮
return SSAValue(0)
end::Array{Float64,2}

Julia: Best practice to unpack parameters inside a function

I can unpack a tuple. I'm trying to write a function (or macro) that would unpack a subset of these from an instance of the type-constructor Parameters(). That is, I know how to do:
a,b,c = unpack(p::Parameters)
But I would like to do something like this:
b,c = unpack(p::Parameters, b,c)
or maybe even lazier:
unpack(p::Parameters, b, c)
This is to avoid writing things like:
function unpack_all_oldstyle(p::Parameters)
a=p.a; b=p.b; c=p.c; ... z=p.z;
return a,b,c,...,z
end
There's something wrong with my approach, but hopefully there is a fix.
In case it wasn't clear from the wording of my question, I'm a total ignoramus. I read about unpacking the ellipsis here: how-to-pass-tuple-as-function-arguments
"module UP tests Unpacking Parameters"
module UP
struct Parameters
a::Int64
b::Int64
c::Int64
end
"this method sets default parameters and returns a tuple of default values"
function Parameters(;
a::Int64 = 3,
b::Int64 = 11,
c::Int64 = 101
)
Parameters(a, b, c)
end
"this function unpacks all parameters"
function unpack_all(p::Parameters)
return p.a, p.b, p.c
end
"this function tests the unpacking function: in the body of the function one can now refer to a rather than p.a : worth the effort if you have dozens of parameters and complicated expressions to compute, e.g. type (-b+sqrt(b^2-4*a*c))/2/a instead of (-p.b+sqrt(p.b^2-4*p.a *p.c))/2/p.a"
function unpack_all_test(p::Parameters)
a, b, c = unpack_all(p)
return a, b, c
end
"""
This function is intended to unpack selected parameters. The first, unnamed argument is the constructor for all parameters. The second argument is a tuple of selected parameters.
"""
function unpack_selected(p::Parameters; x...)
return p.x
end
function unpack_selected_test(p::Parameters; x...)
x = unpack_selected(p, x)
return x
end
export Parameters, unpack_all, unpack_all_test, unpack_selected, unpack_selected_test
end
p = UP.Parameters() # make an instance
UP.unpack_all_test(p)
## (3,11,101) ## Test successful
UP.unpack_selected_test(p, 12)
## 12 ## intended outcome
UP.unpack_selected_test(p, b)
## 11 ## intended outcome
UP.unpack_selected_test(p, c, b, a)
## (101,11,3) ## intended outcome
There already exists one: Parameters.jl.
julia> using Parameters
julia> struct Params
a::Int64
b::Int64
c::Int64
end
julia> #unpack a, c = Params(1,2,3)
Params(1,2,3)
julia> a,c
(1,3)
julia> #with_kw struct Params
a::Int64 = 3
b::Int64 = 11
c::Int64 = 101
end
julia> #unpack c,b,a = Params()
Params
a: Int64 3
b: Int64 11
c: Int64 101
julia> c,b,a
(101,11,3)
BTW, you can fix your unpack_selected by:
unpack_selected(p::Parameters, fields...) = map(x->getfield(p, x), fields).
# note that, the selected field names should be Symbol here
julia> unpack_selected(p, :b)
(11,)
julia> unpack_selected(p, :c, :b, :a)
(101,11,3)

Julia pi approximation slow

I have pi approximation code very similar to that on official page:
function piaprox()
sum = 1.0
for i = 2:m-1
sum = sum + (1.0/(i*i))
end
end
m = parse(Int,ARGS[1])
opak = parse(Int,ARGS[2])
#time for i = 0:opak
piaprox()
end
When I try to compare time of C and Julia, then Julia is significantly slower, almost 38 sec for m = 100000000 (time of C is 0.1608328933 sec). Why this is happening?
julia> m=100000000
julia> function piaprox()
sum = 1.0
for i = 2:m-1
sum = sum + (1.0/(i*i))
end
end
piaprox (generic function with 1 method)
julia> #time piaprox()
28.482094 seconds (600.00 M allocations: 10.431 GB, 3.28% gc time)
I would like to mention two very important paragraphs from Performance Tips section of julia documentation:
Avoid global variables A global variable might have its value, and
therefore its type, change at any point. This makes it difficult for
the compiler to optimize code using global variables. Variables should
be local, or passed as arguments to functions, whenever possible.....
The macro #code_warntype (or its function variant code_warntype()) can
sometimes be helpful in diagnosing type-related problems.
julia> #code_warntype piaprox();
Variables:
sum::Any
#s1::Any
i::Any
It's clear from #code_warntype output that compiler could not recognize types of local variables in piaprox(). So we try to declare types and remove global variables:
function piaprox(m::Int)
sum::Float64 = 1.0
i::Int = 0
for i = 2:m-1
sum = sum + (1.0/(i*i))
end
end
julia> #time piaprox(100000000 )
0.009023 seconds (11.10 k allocations: 399.769 KB)
julia> #code_warntype piaprox(100000000);
Variables:
m::Int64
sum::Float64
i::Int64
#s1::Int64
EDIT
as #user3662120 commented, the super fast behavior of the answer is result of a mistake, without a return value LLVM might ignore the for loop, by adding a return line the #time result would be:
julia> #time piaprox(100000000)
0.746795 seconds (11.11 k allocations: 400.294 KB, 0.45% gc time)
1.644934057834575

How to annotate type using the splat operator

How (is it possible) to annotate the type of arguments when using the splat operator?
f(x, y) = x^2 + y^2
vec = [1.0, 2.0, 'a']
f(vec[1:2]...)
How can I annotate that use of ... in the function call. Also notice that none of the macros to view code (#code_llvm, #code_lowered, #code_native, #code_typed, #code_warntype) work, so it would be very hard to optimize when using the splat?
Because it seems that in the above use-case, macro versions of reflection functions couldn't reach the right argument types, using original function instead of macro, could be helpful:
f(x, y) = x^2 + y^2
vec = [1.0, 2.0, 'a']
#code_warntype(f(vec[1:2]...)) # => Nothing
code_warntype(f,map(typeof,vec[1:2]))
# Variables:
# x::Float64
# y::Float64
# .....
This logic is true for all reflection macros, using their variant function with a (function, collection of types).
references:
The macro #code_warntype has function variant: #code_warntype
How macros generated: macro generator
Util function to reach types: gen_call_with_extracted_types
I think you mean something like this:
julia> foo(args::Float64...) = sum([x^2 for x in args])::Float64
foo (generic function with 1 method)
julia> foo(args::Vector{Float64}) = foo(args...)::Float64
foo (generic function with 2 methods)
julia> foo(args::Tuple{Vararg{Float64}}) = foo(args...)::Float64
foo (generic function with 3 methods)
julia> foo(2.0, 5.5, 7.0)
83.25
julia> v = Float64[2, 5.5, 7.0]
3-element Array{Float64,1}:
2.0
5.5
7.0
julia> foo(v)
83.25
julia> t = tuple(v...)
(2.0,5.5,7.0)
julia> foo(t)
83.25
I've placed type anotations in several places, so you can get the feel of the possibilities.
julia> #which foo(2.0, 5.5, 7.0)
foo(args::Float64...) at none:1
julia> #which foo(v)
foo(args::Array{Float64,1}) at none:1
julia> #which foo(t)
foo(args::Tuple{Vararg{Float64}}) at none:1
#code_warntype, etc.
julia> #code_warntype foo(2.0, 5.5, 7.0)
Variables:
args::Tuple{Float64,Float64,Float64}
#s33::Int64
#s32::Int64
#s31::Int64
x::Float64
#s30::Int64
Body:
begin # none, line 1:
GenSym(1) = (Base.nfields)(args::Tuple{Float64,Float64,Float64})::Int64
0:
GenSym(3) = (top(ccall))(:jl_alloc_array_1d,(top(apply_type))(Base.Array,Float64,1)::Type{Array{Float64,1}},(top(svec))(Base.Any,Base.Int)::SimpleVector,Array{Flo
at64,1},0,GenSym(1),0)::Array{Float64,1}
#s33 = 1
#s32 = 1
#s31 = 0
unless (Base.box)(Base.Bool,(Base.not_int)(#s31::Int64 === GenSym(1)::Bool)::Any)::Bool goto 2
3:
#s31 = (Base.box)(Base.Int,(Base.add_int)(#s31::Int64,1)::Any)::Int64
GenSym(10) = (Base.getfield)(args::Tuple{Float64,Float64,Float64},#s32::Int64)::Float64
GenSym(11) = (Base.box)(Base.Int,(Base.add_int)(#s32::Int64,1)::Any)::Int64
#s30 = 1
GenSym(12) = GenSym(10)
GenSym(13) = (Base.box)(Base.Int,(Base.add_int)(1,1)::Any)::Int64
x = GenSym(12)
#s30 = GenSym(13)
GenSym(14) = GenSym(11)
GenSym(15) = (Base.box)(Base.Int,(Base.add_int)(2,1)::Any)::Int64
#s32 = GenSym(14)
#s30 = GenSym(15)
GenSym(4) = (Base.box)(Base.Float64,(Base.mul_float)(x::Float64,x::Float64)::Any)::Float64
$(Expr(:type_goto, 0, GenSym(4)))
$(Expr(:boundscheck, false))
(Base.arrayset)(GenSym(3),GenSym(4),#s33::Int64)::Array{Float64,1}
$(Expr(:boundscheck, :(Main.pop)))
#s33 = (Base.box)(Base.Int,(Base.add_int)(#s33::Int64,1)::Any)::Int64
4:
unless (Base.box)(Base.Bool,(Base.not_int)((Base.box)(Base.Bool,(Base.not_int)(#s31::Int64 === GenSym(1)::Bool)::Any)::Bool)::Any)::Bool goto 3
2:
1:
GenSym(8) = GenSym(3)
return (Base._mapreduce)($(Expr(:new, :((top(getfield))(Base,:IdFun)::Type{Base.IdFun}))),$(Expr(:new, :((top(getfield))(Base,:AddFun)::Type{Base.AddFun}))),GenSy
m(8))::Float64
end::Float64
julia> #code_warntype foo(v)
Variables:
args::Array{Float64,1}
Body:
begin # none, line 1:
return (top(_apply))((top(getfield))(Main,:call)::F,Main.foo,args::Array{Float64,1})::Float64
end::Float64
julia> #code_warntype foo(t)
Variables:
args::Tuple{Float64,Float64,Float64}
Body:
begin # none, line 1:
return (Main.foo)((top(getfield))(args::Tuple{Float64,Float64,Float64},1)::Float64,(top(getfield))(args::Tuple{Float64,Float64,Float64},2)::Float64,(top(getfield))(args::Tuple{Float64,Float64,Float64},3)::Float64)::Float64
end::Float64
Edit: IJulia notebook, tested at juliabox.org, Julia v0.4.1:
http://nbviewer.ipython.org/gist/Ismael-VC/edeb2f919c2341cb389c
You can also put type annotations when calling a function:
julia> #which foo(t::Tuple{Vararg{Float64}}...)
foo(args::Tuple{Vararg{Float64}}) at none:1
julia> #which foo(v::Vector{Float64}...)
foo(args::Array{Float64,1}) at none:1

Resources