なんちゃってhyperfunctionsの実装を例に函数をパラメーターとする function-like objects についての調査を続行する. Part 1 にあたるノートブックは次の場所にある.
http://nbviewer.jupyter.org/gist/genkuroki/5bbf71cfe3d30fb8b2de9a586bc01b3d
using BenchmarkTools
using PyPlot
function integral(f, a, b)
quadgk(x->f.₊(x + eps()*im), a, b)[1] - quadgk(x->f.₋(x - eps()*im), a, b)[1]
end
function plothf(f, a, b; hfname="", figsize=(6.4,4))
x = linspace(a, b, 401)
figure(figsize=figsize)
plot(x, real.(f.(x)), label="real part")
plot(x, imag.(f.(x)), label="imaginary part", lw=1, ls="--")
grid()
legend()
xlabel("x")
if hfname == ""
hfname = "$f"
end
title(hfname, fontsize=8)
end
function test_of_function(f; N = 10^7)
x = linspace(-10,10,N)
f.(x)
end
test_of_function (generic function with 1 method)
mutable struct Hyperfunction_naive
F::Function
G::Function
end
Hyperfunction_naive(F) = Hyperfunction_naive(F,F)
(f::Hyperfunction_naive)(x) = f.F(x + im*eps()) - f.G(x - im*eps())
F_delta(z) = -1/(2π*im*z)
delta_mutable = Hyperfunction_naive(F_delta)
@show delta_mutable
@show typeof(delta_mutable)
@show delta_mutable(0.0)
@show delta_mutable(eps())
plothf(delta_mutable, -0.1, 0.1)
@benchmark test_of_function(delta_mutable)
delta_mutable = Hyperfunction_naive(F_delta, F_delta) typeof(delta_mutable) =
Hyperfunction_naive delta_mutable(0.0) = 1.4335402848056648e15 + 0.0im delta_mutable(eps()) = 7.167701424028324e14 + 0.0im
BenchmarkTools.Trial: memory estimate: 1.94 GiB allocs estimate: 60000031 -------------- minimum time: 2.068 s (9.48% GC) median time: 2.184 s (12.36% GC) mean time: 2.158 s (12.55% GC) maximum time: 2.224 s (15.58% GC) -------------- samples: 3 evals/sample: 1
@code_warntype delta_mutable(0.0)
Variables: f::Hyperfunction_naive x::Float64 re::Float64 Body: begin SSAValue(3) = (Core.getfield)(f::Hyperfunction_naive, :F)::F SSAValue(5) = (Base.select_value)((Core.getfield)(Main.im, :re)::Bool, 2.220446049250313e-16, (Base.copysign_float)((Base.sitofp)(Float64, 0)::Float64, 2.220446049250313e-16)::Float64)::Float64 SSAValue(6) = (Base.select_value)((Core.getfield)(Main.im, :im)::Bool, 2.220446049250313e-16, (Base.copysign_float)((Base.sitofp)(Float64, 0)::Float64, 2.220446049250313e-16)::Float64)::Float64 SSAValue(4) = (SSAValue(3))($(Expr(:new, Complex{Float64}, :((Base.add_float)(x, SSAValue(5))::Float64), SSAValue(6))))::Any SSAValue(1) = (Core.getfield)(f::Hyperfunction_naive, :G)::F SSAValue(7) = (Base.select_value)((Core.getfield)(Main.im, :re)::Bool, 2.220446049250313e-16, (Base.copysign_float)((Base.sitofp)(Float64, 0)::Float64, 2.220446049250313e-16)::Float64)::Float64 SSAValue(8) = (Base.select_value)((Core.getfield)(Main.im, :im)::Bool, 2.220446049250313e-16, (Base.copysign_float)((Base.sitofp)(Float64, 0)::Float64, 2.220446049250313e-16)::Float64)::Float64 $(Expr(:inbounds, false)) # meta: location complex.jl - 262 re::Float64 = (Base.sub_float)(x::Float64, SSAValue(7))::Float64 # meta: pop location $(Expr(:inbounds, :pop)) return (SSAValue(4) - (SSAValue(1))($(Expr(:new, Complex{Float64}, :(re), :((Base.neg_float)(SSAValue(8))::Float64))))::Any)::Any end::Any
以下のように自由に変更可能である.
FF(z) = -1/(2π*im*z)+1.0
GG(z) = -1/(2π*im*z)+1.0
delta_mutable.F = FF
delta_mutable.G = GG
@show delta_mutable
@show typeof(delta_mutable)
@show delta_mutable(0.0)
@show delta_mutable(eps())
plothf(delta_mutable, -0.1, 0.1)
@benchmark test_of_function(delta_mutable)
delta_mutable = Hyperfunction_naive(FF, GG) typeof(delta_mutable) = Hyperfunction_naive delta_mutable(0.0) = 1.4335402848056648e15 + 0.0im delta_mutable(eps()) = 7.167701424028324e14 + 0.0im
BenchmarkTools.Trial: memory estimate: 1.94 GiB allocs estimate: 60000031 -------------- minimum time: 2.175 s (9.46% GC) median time: 2.252 s (12.62% GC) mean time: 2.254 s (12.56% GC) maximum time: 2.335 s (15.40% GC) -------------- samples: 3 evals/sample: 1
mutable struct Hyperfunction{T<:Function, S<:Function}
F::T
G::S
end
Hyperfunction(F) = Hyperfunction(F,F)
(f::Hyperfunction)(x) = f.F(x + im*eps()) - f.G(x - im*eps())
F_delta(z) = -1/(2π*im*z)
delta = Hyperfunction(F_delta)
@show delta
@show typeof(delta)
@show delta(0.0)
@show delta(eps())
plothf(delta, -0.1, 0.1)
@benchmark test_of_function(delta)
delta = Hyperfunction{#F_delta,#F_delta}(F_delta, F_delta) typeof(delta) = Hyperfunction{#F_delta,#F_delta} delta(0.0) = 1.4335402848056648e15 + 0.0im delta(eps()) = 7.167701424028324e14 + 0.0im
BenchmarkTools.Trial: memory estimate: 152.59 MiB allocs estimate: 3 -------------- minimum time: 417.842 ms (2.70% GC) median time: 426.720 ms (2.65% GC) mean time: 440.119 ms (6.41% GC) maximum time: 490.096 ms (15.96% GC) -------------- samples: 12 evals/sample: 1
以上のように5倍くらい速くなっている.
@code_warntype delta(0.0)
Variables: f <optimized out> x::Float64 re::Float64 Body: begin SSAValue(4) = (Base.select_value)((Core.getfield)(Main.im, :re)::Bool, 2.220446049250313e-16, (Base.copysign_float)((Base.sitofp)(Float64, 0)::Float64, 2.220446049250313e-16)::Float64)::Float64 SSAValue(5) = (Base.select_value)((Core.getfield)(Main.im, :im)::Bool, 2.220446049250313e-16, (Base.copysign_float)((Base.sitofp)(Float64, 0)::Float64, 2.220446049250313e-16)::Float64)::Float64 SSAValue(2) = $(Expr(:invoke, MethodInstance for F_delta(::Complex{Float64}), :($(QuoteNode(F_delta))), :($(Expr(:new, Complex{Float64}, :((Base.add_float)(x, SSAValue(4))::Float64), SSAValue(5)))))) SSAValue(6) = (Base.select_value)((Core.getfield)(Main.im, :re)::Bool, 2.220446049250313e-16, (Base.copysign_float)((Base.sitofp)(Float64, 0)::Float64, 2.220446049250313e-16)::Float64)::Float64 SSAValue(7) = (Base.select_value)((Core.getfield)(Main.im, :im)::Bool, 2.220446049250313e-16, (Base.copysign_float)((Base.sitofp)(Float64, 0)::Float64, 2.220446049250313e-16)::Float64)::Float64 $(Expr(:inbounds, false)) # meta: location complex.jl - 262 re::Float64 = (Base.sub_float)(x::Float64, SSAValue(6))::Float64 # meta: pop location $(Expr(:inbounds, :pop)) SSAValue(3) = $(Expr(:invoke, MethodInstance for F_delta(::Complex{Float64}), :($(QuoteNode(F_delta))), :($(Expr(:new, Complex{Float64}, :(re), :((Base.neg_float)(SSAValue(7))::Float64)))))) return $(Expr(:new, Complex{Float64}, :((Base.sub_float)((Core.getfield)(SSAValue(2), :re)::Float64, (Core.getfield)(SSAValue(3), :re)::Float64)::Float64), :((Base.sub_float)((Core.getfield)(SSAValue(2), :im)::Float64, (Core.getfield)(SSAValue(3), :im)::Float64)::Float64))) end::Complex{Float64}
次のように変更することは可能である.
F_delta(z) = -1/(2π*im*z) + sin(z)
delta.F = F_delta
delta.G = F_delta
@show delta
@show typeof(delta)
@show delta(0.0)
@show delta(eps())
plothf(delta, -0.1, 0.1)
delta = Hyperfunction{#F_delta,#F_delta}(F_delta, F_delta) typeof(delta) = Hyperfunction{#F_delta,#F_delta} delta(0.0) = 1.4335402848056648e15 + 4.440892098500626e-16im delta(eps()) = 7.167701424028324e14 + 0.0im
PyObject Text(0.5,1,'Hyperfunction{#F_delta,#F_delta}(F_delta, F_delta)')
次のように自由には変更できない.
FF(z) = -1/(2π*im*z)+1.0
GG(z) = -1/(2π*im*z)+1.0
delta.F = FF
delta.G = GG
@show delta_mutable
@show typeof(delta_mutable)
@show delta(0.0)
@show delta(eps())
plothf(delta, -0.1, 0.1)
MethodError: Cannot `convert` an object of type #FF to an object of type #F_delta This may have arisen from a call to the constructor #F_delta(...), since type constructors fall back to convert methods. Stacktrace: [1] include_string(::String, ::String) at .\loading.jl:522
F1(z) = sin(z)
G1(z) = sin(z+π/6)
f1 = Hyperfunction(F1, G1)
@show f1
@show typeof(f1)
@show f1(0.0)
plothf(f1, -2π, 2π)
f1 =
Hyperfunction{#F1,#G1}(F1, G1) typeof(f1) = Hyperfunction{#F1,#G1} f1(0.0) = -0.49999999999999994 + 4.1434087356338774e-16im
PyObject Text(0.5,1,'Hyperfunction{#F1,#G1}(F1, G1)')
F2(z) = -sqrt(-z)/(2im)
f2 = Hyperfunction(F2)
@show f2
@show typeof(f2)
@show f2(0.0)
plothf(f2, -1, 4)
f2 = Hyperfunction{#F2,#F2}(F2, F2) typeof(f2) = Hyperfunction{#F2,#F2} f2(0.0) = 1.0536712127723507e-8 + 0.0im
PyObject Text(0.5,1,'Hyperfunction{#F2,#F2}(F2, F2)')
F3(z) = -(-z)^(1.3/π)/(2im)
f3 = Hyperfunction(F3)
@show f3
@show typeof(f3)
@show f3(0.0)
plothf(f3, -2, 2)
f3 = Hyperfunction{#F3,#F3}(F3, F3) typeof(f3) = Hyperfunction{#F3,#F3}
f3(0.0) = 2.0155867032563465e-7 + 0.0im
PyObject Text(0.5,1,'Hyperfunction{#F3,#F3}(F3, F3)')