To expand a bit on what you will be missing out on if you declare your argument types to be Float64
:
ForwardDiff.jl uses Dual
numbers to compute derivatives. If you restrict your argument type to Float64
it will throw an exception:
julia> supertype(ForwardDiff.Dual)
Real
julia> f(x::Vector{Float64}) = sin.(x)
f (generic function with 1 method)
julia> f([1.0,2.0,3.0])
3-element Vector{Float64}:
0.8414709848078965
0.9092974268256817
0.1411200080598672
julia> ForwardDiff.jacobian(f,[1.0,2.0,3.0])
ERROR: MethodError: no method matching f(::Vector{ForwardDiff.Dual{ForwardDiff.Tag{typeof(f), Float64}, Float64, 3}})
The function `f` exists, but no method is defined for this combination of argument types.
If you want to use automatic differentiation then you should type your arguments at least Real
.
julia> f2(x::Vector{T}) where{T<:Real}= sin.(x)
f2 (generic function with 2 methods)
julia> ForwardDiff.jacobian(f2,[1.0,2.0,3.0])
3Ă3 Matrix{Float64}:
0.540302 0.0 0.0
-0.0 -0.416147 -0.0
-0.0 -0.0 -0.989992
If you want to use Unitful
arguments (meters, seconds, kilograms, etc.) then âRealâ is too restrictive
using Unitful,Unitful.DefaultSymbols
julia> supertype(typeof(1.0m))
Unitful.AbstractQuantity{Float64, đ, Unitful.FreeUnits{(m,), đ, nothing}}
julia> supertype(ans)
Number
In this case you have to use the Number
type:
julia> f3(x::Vector{T}) where{T<:Number} = x .* x
f3 (generic function with 1 method)
julia> f3([1.0m,2.0m,3.0m])
3-element Vector{Quantity{Float64, đ^2, Unitful.FreeUnits{(m^2,), đ^2, nothing}}}:
1.0 m^2
4.0 m^2
9.0 m^2
If you use interval arithmetic (IntervalArithmetic.jl) then you need to make the argument types at least Real
:
julia> using IntervalArithmetic
julia> supertype(Interval)
Real
julia> x = ([interval(0,3),interval(0,Inf)])
2-element Vector{Interval{Float64}}:
[0.0, 3.0]_com
[0.0, â)_dac
julia> f(x)
ERROR: MethodError: no method matching f(::Vector{Interval{Float64}})
The function `f` exists, but no method is defined for this combination of argument types.
julia> f2(x)
2-element Vector{Interval{Float64}}:
[-0.841472, -0.84147]_com
[-1.0, 1.0]_dac
julia> f3(x)
2-element Vector{Interval{Float64}}:
[1.0, 1.0]_com
[0.0, â)_dac
You may want to do error analysis using a package like âMeasurement.jlâ:
julia> using Measurement
julia> supertype(Measurement)
AbstractFloat
julia> h(x) = x^2
h (generic function with 1 method)
julia> ForwardDiff.derivative(h,1.0 ± .01)
2.0 ± 0.02
In this case you need to make the argument type at least `AbstractFloatâ. Also notice that this example mixes the use of two special number types, âMeasurementâ and âDualâ. Magically they work together seamlessly.
You can easily plot measurement values with error bars without any effort. Just pass Measurement
type numbers to plot
:
plot([1.0±.1,2.0±.3,3.0±.25])
which gives you this:
There are many more such special number types that provide, for me at least, incredibly useful functionality.
It is not practical to use a Union type to include all of them because A) you donât know what they all are nor do you know which of them you might want to use in the future, and B) more of them are being created all the time.
Imagine youâve decided on the set of special numbers you want to allow as arguments:
julia> f(x::Union{Measurement,Dual,Interval,BigFloat,Float64,Float32})
Now imagine you decide you want to do symbolic analysis on your functions using Symbolics.jl. You have to change every declaration of every function to add Num
, which is the type of variables in Symbolics.
Whereas if you had declared your argument types Number
you could just do the following:
julia> using Symbolics
julia> @variables z
1-element Vector{Num}:
z
julia> typeof(z)
Num
julia> f3([z,z])
2-element Vector{Num}:
z^2
z^2
If you are certain you will never want to use any of these special number types then what you are suggesting could be reasonable. But you will be giving up an incredible amount of power and flexibility.