Peter
DL situation!!
struct Dual{T<:Real} <: Real
x::T
ϵ::T
end
import Base: +, -, *, /
a::Dual + b::Dual = Dual(a.x + b.x, a.ϵ + b.ϵ)
a::Dual - b::Dual = Dual(a.x - b.x, a.ϵ - b.ϵ)
a::Dual * b::Dual = Dual(a.x * b.x, b.x * a.ϵ + a.x * b.ϵ)
a::Dual / b::Dual = Dual(a.x * b.x, b.x * a.ϵ - a.x * b.ϵ)
Base.sin(d::Dual) = Dual(sin(d.x), d.ϵ * cos(d.x))
Base.cos(d::Dual) = Dual(cos(d.x), - d.ϵ * sin(d.x))
Base.log(d::Dual) = Dual(log(d.x), d.ϵ / d.x)
Base.convert(::Type{Dual{T}}, x::Dual) where T = Dual(convert(T, x.x), convert(T, x.ϵ))
Base.convert(::Type{Dual{T}}, x::Real) where T = Dual(convert(T, x), zero(T))
Base.promote_rule(::Type{Dual{T}}, ::Type{R}) where {T,R} = Dual{promote_type(T,R)}
D(f, x) = f(Dual(x, one(x))).ϵ
f(x) = 5*sin(log(x))
df(x) = 5*cos(log(x))/x
D(f, 3.) == df(3.)
<- TensorFlow
Pytorch, ->
eager mode
julia> @code_warntype f(3.)
Body::Float64
1 ─ %1 = invoke Main.log(_2::Float64)::Float64
│ %2 = invoke Main.sin(%1::Float64)::Float64
│ %3 = (Base.mul_float)(5.0, %2)::Float64
└── return %3
julia> @code_warntype D(f, 3)
Body::Float64
1 ─ %1 = (Base.sitofp)(Float64, x)::Float64
│ %2 = invoke Base.Math.log(%1::Float64)::Float64
│ %3 = (Base.sitofp)(Float64, 1)::Float64
│ %4 = (Base.sitofp)(Float64, x)::Float64
│ %5 = (Base.div_float)(%3, %4)::Float64
│ %6 = invoke Main.sin(%2::Float64)::Float64
│ %7 = invoke Main.cos(%2::Float64)::Float64
│ %8 = (Base.mul_float)(%5, %7)::Float64
│ %9 = (Base.mul_float)(%6, 0.0)::Float64
│ %10 = (Base.mul_float)(5.0, %8)::Float64
│ %11 = (Base.add_float)(%9, %10)::Float64
└── return %11
julia> @code_warntype f(3.)
Body::Float64
1 ─ %1 = invoke Main.log(_2::Float64)::Float64
│ %2 = invoke Main.sin(%1::Float64)::Float64
│ %3 = (Base.mul_float)(5.0, %2)::Float64
└── return %3
julia> @code_warntype D(f, 3)
Body::Float64
1 ─ %1 = (Base.sitofp)(Float64, x)::Float64
│ %2 = invoke Base.Math.log(%1::Float64)::Float64
│ %3 = (Base.sitofp)(Float64, 1)::Float64
│ %4 = (Base.sitofp)(Float64, x)::Float64
│ %5 = (Base.div_float)(%3, %4)::Float64
│ %6 = invoke Main.sin(%2::Float64)::Float64
│ %7 = invoke Main.cos(%2::Float64)::Float64
│ %8 = (Base.mul_float)(%5, %7)::Float64
│ %9 = (Base.mul_float)(%6, 0.0)::Float64
│ %10 = (Base.mul_float)(5.0, %8)::Float64
│ %11 = (Base.add_float)(%9, %10)::Float64
└── return %11
julia> using Zygote
julia> f(x) = 5x + 3
julia> f(10), f'(10)
(53, 5)
julia> @code_llvm f'(10)
define i64 @"julia_#625_38792"(i64) {
top:
ret i64 5
}
julia> f(x) = 5*sin(log(x))
f (generic function with 1 method)
julia> f'
#34 (generic function with 1 method)
julia> f'(3.)
0.7580540380443495
julia> @code_llvm f'(3.)
define double @"julia_#34_13685"(double) {
top:
%1 = call double @julia_log_4663(double %0)
%2 = call double @julia_sin_13686(double %1)
%3 = call double @julia_cos_13689(double %1)
%4 = fmul double %3, 5.000000e+00
%5 = fdiv double 1.000000e+00, %0
%6 = fmul double %5, %4
ret double %6
}
julia> @code_native f'(3.)
...
julia> f(x) = 5*sin(log(x))
f (generic function with 1 method)
julia> f'
#34 (generic function with 1 method)
julia> f'(3.)
0.7580540380443495
julia> ddf(x) = -(5(sin(log(x)) + cos(log(x))))/x^2
ddf (generic function with 1 method)
julia> ddf(3.)
-0.7474497024968649
julia> (f')'(3.)
-0.7474497024968648
julia> fs = Dict("sin" => sin, "cos" => cos, "tan" => tan);
julia> gradient(x -> fs[readline()](x), 1)
sin
0.5403023058681398
julia> function pow(x::T, n::Int) where T
r = 1::T
while n > 0
n -= 1
r *= x
end
return r
end
pow (generic function with 1 method)
julia> g(x) = pow(x, 5)
g (generic function with 1 method)
julia> g'(2)
80
julia> gradient(pow, 2, 5)
(80, nothing)
julia> using Zygote: @adjoint
julia> add(a, b) = a + b
julia> @adjoint add(a, b) = add(a, b), Δ -> (Δ, Δ)
function grad_pow(x, n)
r = 1
Bs = Tuple{Int, Int}[]
while n > 0
push!(Bs, (r, x))
r *= x
n -= 1
end
dx = 0
dr = 1
for i = length(Bs):-1:1
(r, x) = Bs[i]
dx += dr*r
dr = dr*x
end
return dx
end
function pow(x, n)
r = 1
while n > 0
n -= 1
r *= x
end
return r
end
Zygote