Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes for 0.7; includes a possibly non-kosher API change #32

Closed
wants to merge 11 commits into from
Closed

Changes for 0.7; includes a possibly non-kosher API change #32

wants to merge 11 commits into from

Conversation

chriselrod
Copy link

In trying to clean a few things up for 0.7, I just started replacing Val{T}s with Val(T) or Val{T}() following the new documentation:
https://docs.julialang.org/en/latest/manual/types/#%22Value-types%22-1
This turned out to be a bigger change than I intended, especially because the value types are a part of the external API.
Therefore I added an "old API" file, which should stop dependencies from breaking so long as they don't construct Cache objects directly.

@codecov
Copy link

codecov bot commented Mar 23, 2018

Codecov Report

Merging #32 into master will increase coverage by 39.11%.
The diff coverage is 100%.

Impacted file tree graph

@@             Coverage Diff             @@
##           master      #32       +/-   ##
===========================================
+ Coverage   60.05%   99.16%   +39.11%     
===========================================
  Files           5        5               
  Lines         383      240      -143     
===========================================
+ Hits          230      238        +8     
+ Misses        153        2      -151
Impacted Files Coverage Δ
src/finitediff.jl 100% <100%> (+38.46%) ⬆️
src/gradients.jl 98.21% <100%> (+45.32%) ⬆️
src/derivatives.jl 100% <100%> (+30.76%) ⬆️
src/old_val_api.jl 100% <100%> (ø)
src/jacobians.jl 100% <100%> (+23.86%) ⬆️
src/function_wrappers.jl

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update cd80e68...633a91c. Read the comment docs.

@coveralls
Copy link

coveralls commented Mar 23, 2018

Coverage Status

Coverage increased (+39.1%) to 99.167% when pulling 254dd7f on chriselrod:master into cd80e68 on JuliaDiffEq:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+39.09%) to 99.138% when pulling d4b39d6 on chriselrod:master into cd80e68 on JuliaDiffEq:master.

@ChrisRackauckas
Copy link
Member

Can this get inference tests on v0.7 to make sure that it's all good still? That of course is the main thing to worry about: speed an inference.

@chriselrod
Copy link
Author

I added inference tests, and also another file testing the old interface (both files run the same tests).

@@ -1,15 +1,15 @@
#=
Single-point derivatives of scalar->scalar maps.
=#
function finite_difference_derivative(f, x::T, fdtype::Type{T1}=Val{:central},
returntype::Type{T2}=eltype(x), f_x::Union{Void,T}=nothing) where {T<:Number,T1,T2}
function finite_difference_derivative(f, x::T, ::Val{fdtype}=Val{:central}(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why instances instead?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I switched to instances of Val, because the documentation on value types changed from 0.6 to 0.7:
https://docs.julialang.org/en/stable/manual/types/#"Value-types"-1
https://docs.julialang.org/en/latest/manual/types/#"Value-types"-1

In particular, from the latest docs:

For consistency across Julia, the call site should always pass a Valinstance rather than using a type, i.e., use foo(Val(:bar)) rather than foo(Val{:bar}).

The stable / current docs reccomend passing around Val{:central}, and dispatching using f(::Type{Val{:central}}).

Figured I'd update the interface to match the new recommendation, and that instances would eventually become more familiar / expected.

Do you have anything formal in mind when it comes to regression testing?

@ChrisRackauckas
Copy link
Member

Since this is a big change that is relying on new compiler changes, can we get some v0.7 performance tests? Also, we should make sure PRs to downstream libraries are lined up.

@chriselrod
Copy link
Author

chriselrod commented Mar 26, 2018

A few comments, since what follows is a giant wall of numbers. The first set is with the current master, and the next set with this PR. If you want to look at them, I'd suggest two windows side by side to compare them with.

Unfortunately, there seem to be minor regressions consistently across the board (possibly noise?), and -- while I didn't comb through all of it -- a pretty major one with:

@benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x)

towards the end. Allocations increased, as did runtime from about 200ns to 1 microsecond.
These tests are taken from the old interface.
That one was because of:

julia> using DiffEqDiffTools

julia> x = rand(2); y = rand(2);

julia> @code_warntype DiffEqDiffTools.JacobianCache(x, Val{:central}(), eltype(x), Val{true}())
Variables:
  #self# <optimized out>
  x::Array{Float64,1}
  #temp#@_3 <optimized out>
  returntype::Type{Float64}
  #temp#@_5 <optimized out>
  x1::Any
  _fx::Any
  _fx1::Union{Array{Float64,1}, Void}
  #temp#@_9::Bool

Body:
  begin 
      NewvarNode(:(x1::Any))
      NewvarNode(:(_fx::Any))
      NewvarNode(:(_fx1::Union{Array{Float64,1}, Void}))
      (Float64 <: DiffEqDiffTools.Real)::Bool
      #temp#@_9::Bool = ($(Expr(:static_parameter, 2)) === :complex)::Bool
      goto 7
      7: 
      unless #temp#@_9::Bool goto 16 # line 13:
      SSAValue(10) = (Base.arraysize)(x::Array{Float64,1}, 1)::Int64
      x1::Any = $(Expr(:invoke, MethodInstance for fill!(::Array{Complex{Float64},1}, ::Complex{Float64}), :(Base.fill!), :($(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Complex{Float64},1}, svec(Any, Int64), Array{Complex{Float64},1}, 0, SSAValue(10), 0))), :($(Expr(:new, Complex{Float64}, :((Base.sitofp)(Float64, 0)::Float64), :((Base.sitofp)(Float64, 0)::Float64)))))) # line 14:
      SSAValue(11) = (Base.arraysize)(x::Array{Float64,1}, 1)::Int64
      _fx::Any = $(Expr(:invoke, MethodInstance for fill!(::Array{Complex{Float64},1}, ::Complex{Float64}), :(Base.fill!), :($(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Complex{Float64},1}, svec(Any, Int64), Array{Complex{Float64},1}, 0, SSAValue(11), 0))), :($(Expr(:new, Complex{Float64}, :((Base.sitofp)(Float64, 0)::Float64), :((Base.sitofp)(Float64, 0)::Float64))))))
      goto 31
      16:  # line 16:
      $(Expr(:inbounds, false))
      # meta: location array.jl similar 189
      SSAValue(4) = (Base.arraysize)(x::Array{Float64,1}, 1)::Int64
      # meta: pop location
      $(Expr(:inbounds, :pop))
      x1::Any = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Float64,1}, svec(Any, Int64), Array{Float64,1}, 0, SSAValue(4), 0)) # line 17:
      $(Expr(:inbounds, false))
      # meta: location array.jl similar 189
      SSAValue(6) = (Base.arraysize)(x::Array{Float64,1}, 1)::Int64
      # meta: pop location
      $(Expr(:inbounds, :pop))
      _fx::Any = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Float64,1}, svec(Any, Int64), Array{Float64,1}, 0, SSAValue(6), 0))
      31:  # line 20:
      unless ($(Expr(:static_parameter, 2)) === :complex)::Bool goto 37 # line 21:
      _fx1::Union{Array{Float64,1}, Void} = DiffEqDiffTools.nothing
      goto 45
      37:  # line 23:
      $(Expr(:inbounds, false))
      # meta: location array.jl similar 189
      SSAValue(8) = (Base.arraysize)(x::Array{Float64,1}, 1)::Int64
      # meta: pop location
      $(Expr(:inbounds, :pop))
      _fx1::Union{Array{Float64,1}, Void} = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Float64,1}, svec(Any, Int64), Array{Float64,1}, 0, SSAValue(8), 0))
      45:  # line 26:
      return (DiffEqDiffTools.JacobianCache)(x1::Any, _fx::Any, _fx1::Union{Array{Float64,1}, Void}, $(QuoteNode(Val{:central}())), returntype::Type{Float64}, $(QuoteNode(Val{true}())))::Union{DiffEqDiffTools.JacobianCache{Array{Complex{Float64},1},_,Void,:central,Float64,true} where _, DiffEqDiffTools.JacobianCache{_,_,Array{Float64,1},:central,Float64,true} where _ where _}
  end::Union{DiffEqDiffTools.JacobianCache{Array{Complex{Float64},1},_,Void,:central,Float64,true} where _, DiffEqDiffTools.JacobianCache{_,_,Array{Float64,1},:central,Float64,true} where _ where _}

inference is successful on 0.7.

Thoughts, recommendations?

  • Have this PR only support 0.7?
  • Restrict this PR to handling dep warnings of 0.7 only?
  • Break up functions where inference fails manually, to make sure it is successful?

I get that the minor regressions seemingly across the board from this PR would be unpalatable.

Using this computer:

julia> versioninfo()
Julia Version 0.6.2
Commit d386e40 (2017-12-13 18:08 UTC)
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
  WORD_SIZE: 64
  BLAS: libmkl_rt
  LAPACK: libmkl_rt
  LIBM: libimf
  LLVM: libLLVM-3.9.1 (ORCJIT, haswell)

running benchmarks on the old interface file, on DiffEQ master:

julia> using DiffEqDiffTools

julia> using Compat, Compat.LinearAlgebra

julia> using BenchmarkTools

julia> # TODO: add tests for GPUArrays
       # TODO: add tests for DEDataArrays

       # Derivative tests
       x = collect(Compat.range(-2π, stop=2π, length=100));

julia> y = sin.(x);

julia> df = fill(0.0,100);

julia> epsilon = fill(0.0,100);

julia> df_ref = cos.(x);

julia> forward_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:forward});

julia> central_cache = DiffEqDiffTools.DerivativeCache(x, nothing, epsilon, Val{:central});

julia> complex_cache = DiffEqDiffTools.DerivativeCache(x, nothing, nothing, Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, π/4, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     18.543 ns (0.00% GC)
  median time:      18.593 ns (0.00% GC)
  mean time:        18.998 ns (0.00% GC)
  maximum time:     52.111 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, π/4, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     30.094 ns (0.00% GC)
  median time:      30.192 ns (0.00% GC)
  mean time:        31.594 ns (0.00% GC)
  maximum time:     74.412 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     994

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, π/4, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     26.483 ns (0.00% GC)
  median time:      26.530 ns (0.00% GC)
  mean time:        27.202 ns (0.00% GC)
  maximum time:     86.451 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     995

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     1.903 μs (0.00% GC)
  median time:      1.966 μs (0.00% GC)
  mean time:        2.187 μs (6.36% GC)
  maximum time:     218.984 μs (97.52% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.022 μs (0.00% GC)
  median time:      2.062 μs (0.00% GC)
  mean time:        2.322 μs (6.80% GC)
  maximum time:     262.184 μs (96.76% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.683 μs (0.00% GC)
  median time:      2.752 μs (0.00% GC)
  mean time:        3.028 μs (5.18% GC)
  maximum time:     242.825 μs (96.73% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  928 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.315 μs (0.00% GC)
  median time:      1.356 μs (0.00% GC)
  mean time:        1.517 μs (5.69% GC)
  maximum time:     231.571 μs (96.51% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:central}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.013 μs (0.00% GC)
  median time:      2.067 μs (0.00% GC)
  mean time:        2.430 μs (6.90% GC)
  maximum time:     268.206 μs (97.08% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:complex}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.683 μs (0.00% GC)
  median time:      2.754 μs (0.00% GC)
  mean time:        3.042 μs (5.27% GC)
  maximum time:     244.693 μs (96.42% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.859 μs (0.00% GC)
  median time:      1.906 μs (0.00% GC)
  mean time:        2.179 μs (4.39% GC)
  maximum time:     274.980 μs (97.57% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.964 μs (0.00% GC)
  median time:      2.037 μs (0.00% GC)
  mean time:        2.238 μs (3.81% GC)
  maximum time:     230.238 μs (96.53% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     2.641 μs (0.00% GC)
  median time:      2.667 μs (0.00% GC)
  mean time:        2.828 μs (2.49% GC)
  maximum time:     247.384 μs (96.24% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  32 bytes
  allocs estimate:  1
  --------------
  minimum time:     1.265 μs (0.00% GC)
  median time:      1.291 μs (0.00% GC)
  mean time:        1.356 μs (0.00% GC)
  maximum time:     4.494 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:central}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.963 μs (0.00% GC)
  median time:      1.987 μs (0.00% GC)
  mean time:        2.154 μs (3.94% GC)
  maximum time:     226.317 μs (96.47% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:complex}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     2.641 μs (0.00% GC)
  median time:      2.725 μs (0.00% GC)
  mean time:        2.890 μs (2.45% GC)
  maximum time:     245.685 μs (96.84% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     1.042 μs (0.00% GC)
  median time:      1.048 μs (0.00% GC)
  mean time:        1.054 μs (0.00% GC)
  maximum time:     3.875 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     1.904 μs (0.00% GC)
  median time:      1.912 μs (0.00% GC)
  mean time:        1.944 μs (0.00% GC)
  maximum time:     5.728 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     2.581 μs (0.00% GC)
  median time:      2.587 μs (0.00% GC)
  mean time:        2.645 μs (0.00% GC)
  maximum time:     6.868 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> x = x + im*x;

julia> f(x) = sin(x) + cos(x);

julia> y = f.(x);

julia> df = zero(x);

julia> epsilon = similar(real(x));

julia> df_ref = cos.(x) - sin.(x);

julia> forward_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:forward});

julia> central_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:central});
WARNING: Pre-computed function values are only useful for fdtype==Val{:forward}.

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4+im*π/4, Val{:forward}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     155.694 ns (0.00% GC)
  median time:      155.892 ns (0.00% GC)
  mean time:        156.832 ns (0.00% GC)
  maximum time:     290.471 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     767

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4+im*π/4, Val{:central}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     166.265 ns (0.00% GC)
  median time:      166.460 ns (0.00% GC)
  mean time:        167.215 ns (0.00% GC)
  maximum time:     282.874 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     728

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     15.718 μs (0.00% GC)
  median time:      15.923 μs (0.00% GC)
  mean time:        16.646 μs (1.38% GC)
  maximum time:     2.369 ms (97.15% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     15.656 μs (0.00% GC)
  median time:      15.885 μs (0.00% GC)
  mean time:        16.630 μs (1.40% GC)
  maximum time:     2.403 ms (97.10% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, eltype($x), $y)
BenchmarkTools.Trial: 
  memory estimate:  2.67 KiB
  allocs estimate:  3
  --------------
  minimum time:     8.971 μs (0.00% GC)
  median time:      9.199 μs (0.00% GC)
  mean time:        9.742 μs (0.00% GC)
  maximum time:     26.114 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:central}, eltype($x))#, $y)
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     15.615 μs (0.00% GC)
  median time:      15.889 μs (0.00% GC)
  mean time:        16.626 μs (1.40% GC)
  maximum time:     2.392 ms (97.04% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  1.80 KiB
  allocs estimate:  2
  --------------
  minimum time:     9.190 μs (0.00% GC)
  median time:      9.392 μs (0.00% GC)
  mean time:        9.870 μs (0.00% GC)
  maximum time:     32.735 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     15.593 μs (0.00% GC)
  median time:      15.702 μs (0.00% GC)
  mean time:        15.983 μs (0.00% GC)
  maximum time:     32.360 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:central}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     15.537 μs (0.00% GC)
  median time:      15.680 μs (0.00% GC)
  mean time:        15.956 μs (0.00% GC)
  maximum time:     32.774 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, eltype($x), $y)
BenchmarkTools.Trial: 
  memory estimate:  928 bytes
  allocs estimate:  2
  --------------
  minimum time:     8.849 μs (0.00% GC)
  median time:      9.162 μs (0.00% GC)
  mean time:        9.204 μs (0.91% GC)
  maximum time:     869.434 μs (96.77% GC)
  --------------
  samples:          10000
  evals/sample:     3

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  32 bytes
  allocs estimate:  1
  --------------
  minimum time:     9.088 μs (0.00% GC)
  median time:      9.184 μs (0.00% GC)
  mean time:        9.584 μs (0.00% GC)
  maximum time:     36.773 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     8.796 μs (0.00% GC)
  median time:      8.871 μs (0.00% GC)
  mean time:        9.319 μs (0.00% GC)
  maximum time:     16.635 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     3

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     15.466 μs (0.00% GC)
  median time:      15.588 μs (0.00% GC)
  mean time:        15.647 μs (0.00% GC)
  maximum time:     31.980 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> x = collect(Compat.range(-2π, stop=2π, length=100));

julia> f(x) = sin(x) + im*cos(x);

julia> y = f.(x);

julia> df = fill(zero(Complex{eltype(x)}), size(x));

julia> epsilon = similar(real(x));

julia> df_ref = cos.(x) - im*sin.(x);

julia> forward_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:forward}, eltype(df));

julia> central_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:central}, eltype(df));
WARNING: Pre-computed function values are only useful for fdtype==Val{:forward}.

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4, Val{:forward}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     42.843 ns (0.00% GC)
  median time:      42.996 ns (0.00% GC)
  mean time:        43.436 ns (0.00% GC)
  maximum time:     95.756 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     990

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4, Val{:central}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     52.644 ns (0.00% GC)
  median time:      52.784 ns (0.00% GC)
  mean time:        53.612 ns (0.00% GC)
  maximum time:     140.868 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     986

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     4.141 μs (0.00% GC)
  median time:      4.247 μs (0.00% GC)
  mean time:        4.573 μs (4.64% GC)
  maximum time:     373.020 μs (96.85% GC)
  --------------
  samples:          10000
  evals/sample:     7

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:central}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     3.923 μs (0.00% GC)
  median time:      4.013 μs (0.00% GC)
  mean time:        4.319 μs (4.75% GC)
  maximum time:     315.975 μs (96.57% GC)
  --------------
  samples:          10000
  evals/sample:     7

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, Complex{eltype($x)}, $y)
BenchmarkTools.Trial: 
  memory estimate:  2.67 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.469 μs (0.00% GC)
  median time:      2.546 μs (0.00% GC)
  mean time:        2.886 μs (8.02% GC)
  maximum time:     261.536 μs (96.18% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, Complex{eltype($x)}, $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  1.80 KiB
  allocs estimate:  2
  --------------
  minimum time:     2.620 μs (0.00% GC)
  median time:      2.698 μs (0.00% GC)
  mean time:        2.951 μs (5.65% GC)
  maximum time:     251.701 μs (96.80% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     4.038 μs (0.00% GC)
  median time:      4.076 μs (0.00% GC)
  mean time:        4.252 μs (1.49% GC)
  maximum time:     329.684 μs (96.96% GC)
  --------------
  samples:          10000
  evals/sample:     7

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:central}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     3.828 μs (0.00% GC)
  median time:      3.941 μs (0.00% GC)
  mean time:        4.463 μs (1.59% GC)
  maximum time:     389.181 μs (96.08% GC)
  --------------
  samples:          10000
  evals/sample:     7

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, Complex{eltype($x)}, $y)
BenchmarkTools.Trial: 
  memory estimate:  928 bytes
  allocs estimate:  2
  --------------
  minimum time:     2.351 μs (0.00% GC)
  median time:      2.430 μs (0.00% GC)
  mean time:        2.620 μs (2.90% GC)
  maximum time:     262.234 μs (97.01% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, Complex{eltype($x)}, $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  32 bytes
  allocs estimate:  1
  --------------
  minimum time:     2.520 μs (0.00% GC)
  median time:      2.540 μs (0.00% GC)
  mean time:        2.613 μs (0.00% GC)
  maximum time:     6.920 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     2.288 μs (0.00% GC)
  median time:      2.295 μs (0.00% GC)
  mean time:        2.322 μs (0.00% GC)
  maximum time:     5.940 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     3.762 μs (0.00% GC)
  median time:      3.771 μs (0.00% GC)
  mean time:        3.810 μs (0.00% GC)
  maximum time:     9.463 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     8

julia> # Gradient tests
       f(x) = 2x[1] + x[2]^2;

julia> x = rand(2);

julia> fx = f(x);

julia> df = fill(0.0, 2);

julia> df_ref = [2., 2*x[2]];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> complex_cache = DiffEqDiffTools.GradientCache(df,x,Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  96 bytes
  allocs estimate:  1
  --------------
  minimum time:     28.009 ns (0.00% GC)
  median time:      29.355 ns (0.00% GC)
  mean time:        47.661 ns (30.85% GC)
  maximum time:     3.789 μs (97.44% GC)
  --------------
  samples:          10000
  evals/sample:     995

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  96 bytes
  allocs estimate:  1
  --------------
  minimum time:     42.297 ns (0.00% GC)
  median time:      44.503 ns (0.00% GC)
  mean time:        63.380 ns (24.33% GC)
  maximum time:     4.009 μs (97.46% GC)
  --------------
  samples:          10000
  evals/sample:     991

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  224 bytes
  allocs estimate:  3
  --------------
  minimum time:     72.989 ns (0.00% GC)
  median time:      82.773 ns (0.00% GC)
  mean time:        127.346 ns (31.74% GC)
  maximum time:     4.657 μs (96.50% GC)
  --------------
  samples:          10000
  evals/sample:     976

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     8.551 ns (0.00% GC)
  median time:      8.598 ns (0.00% GC)
  mean time:        8.761 ns (0.00% GC)
  maximum time:     35.259 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     999

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     18.211 ns (0.00% GC)
  median time:      18.595 ns (0.00% GC)
  mean time:        18.799 ns (0.00% GC)
  maximum time:     53.610 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  128 bytes
  allocs estimate:  2
  --------------
  minimum time:     52.733 ns (0.00% GC)
  median time:      56.133 ns (0.00% GC)
  mean time:        72.145 ns (20.21% GC)
  maximum time:     2.893 μs (95.90% GC)
  --------------
  samples:          10000
  evals/sample:     986

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     7.040 ns (0.00% GC)
  median time:      7.106 ns (0.00% GC)
  mean time:        7.314 ns (0.00% GC)
  maximum time:     20.310 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     999

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     17.080 ns (0.00% GC)
  median time:      17.119 ns (0.00% GC)
  mean time:        17.641 ns (0.00% GC)
  maximum time:     60.342 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     998

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     19.083 ns (0.00% GC)
  median time:      19.360 ns (0.00% GC)
  mean time:        19.378 ns (0.00% GC)
  maximum time:     46.280 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> f(x) = 2x[1] + im*2x[1] + x[2]^2
f (generic function with 2 methods)

julia> x = x + im*x;

julia> fx = f(x);

julia> df = zero(x);

julia> df_ref = conj([2.0+2.0*im, 2.0*x[2]]);

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward})
DiffEqDiffTools.GradientCache{Void,Void,Void,Val{:forward},Complex{Float64},Val{true}}(nothing, nothing, nothing)

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central})
DiffEqDiffTools.GradientCache{Void,Void,Void,Val{:central},Complex{Float64},Val{true}}(nothing, nothing, nothing)

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     136.010 ns (0.00% GC)
  median time:      137.328 ns (0.00% GC)
  mean time:        151.007 ns (7.54% GC)
  maximum time:     3.183 μs (93.35% GC)
  --------------
  samples:          10000
  evals/sample:     870

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     148.321 ns (0.00% GC)
  median time:      149.536 ns (0.00% GC)
  mean time:        164.138 ns (6.99% GC)
  maximum time:     3.435 μs (92.73% GC)
  --------------
  samples:          10000
  evals/sample:     832

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     118.236 ns (0.00% GC)
  median time:      118.315 ns (0.00% GC)
  mean time:        119.228 ns (0.00% GC)
  maximum time:     235.316 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     911

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     129.363 ns (0.00% GC)
  median time:      129.497 ns (0.00% GC)
  mean time:        130.170 ns (0.00% GC)
  maximum time:     219.565 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     891

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     116.274 ns (0.00% GC)
  median time:      116.314 ns (0.00% GC)
  mean time:        117.215 ns (0.00% GC)
  maximum time:     250.384 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     916

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     125.931 ns (0.00% GC)
  median time:      126.012 ns (0.00% GC)
  mean time:        127.455 ns (0.00% GC)
  maximum time:     274.877 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     896

julia> f(x) = sum(abs2, x)
f (generic function with 2 methods)

julia> x = ones(2) * (1 + im);

julia> fx = f(x);

julia> df = zero(x);

julia> df_ref = 2*x;

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     110.266 ns (0.00% GC)
  median time:      111.298 ns (0.00% GC)
  mean time:        125.831 ns (9.17% GC)
  maximum time:     3.064 μs (94.90% GC)
  --------------
  samples:          10000
  evals/sample:     927

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     97.934 ns (0.00% GC)
  median time:      99.045 ns (0.00% GC)
  mean time:        113.191 ns (10.18% GC)
  maximum time:     2.725 μs (94.57% GC)
  --------------
  samples:          10000
  evals/sample:     949

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     84.274 ns (0.00% GC)
  median time:      84.407 ns (0.00% GC)
  mean time:        85.438 ns (0.00% GC)
  maximum time:     179.598 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     963

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     76.600 ns (0.00% GC)
  median time:      77.240 ns (0.00% GC)
  mean time:        77.925 ns (0.00% GC)
  maximum time:     155.466 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     970

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     83.992 ns (0.00% GC)
  median time:      84.580 ns (0.00% GC)
  mean time:        85.100 ns (0.00% GC)
  maximum time:     148.694 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     963

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     74.882 ns (0.00% GC)
  median time:      75.163 ns (0.00% GC)
  mean time:        75.766 ns (0.00% GC)
  maximum time:     144.155 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     972

julia> f(x) = 2*x[1] + im*x[2]^2
f (generic function with 2 methods)

julia> x = ones(2);

julia> fx = f(x);

julia> df = fill(zero(eltype(fx)), size(x));

julia> df_ref = [2.0, -im*2*x[2]];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward},eltype(df));

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central},eltype(df));

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  240 bytes
  allocs estimate:  3
  --------------
  minimum time:     115.760 ns (0.00% GC)
  median time:      123.409 ns (0.00% GC)
  mean time:        152.235 ns (17.80% GC)
  maximum time:     3.438 μs (94.66% GC)
  --------------
  samples:          10000
  evals/sample:     925

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  240 bytes
  allocs estimate:  3
  --------------
  minimum time:     120.892 ns (0.00% GC)
  median time:      129.429 ns (0.00% GC)
  mean time:        160.898 ns (17.17% GC)
  maximum time:     3.895 μs (92.59% GC)
  --------------
  samples:          10000
  evals/sample:     914

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  128 bytes
  allocs estimate:  2
  --------------
  minimum time:     94.517 ns (0.00% GC)
  median time:      97.501 ns (0.00% GC)
  mean time:        114.778 ns (13.14% GC)
  maximum time:     3.497 μs (94.08% GC)
  --------------
  samples:          10000
  evals/sample:     954

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  128 bytes
  allocs estimate:  2
  --------------
  minimum time:     112.205 ns (0.00% GC)
  median time:      116.867 ns (0.00% GC)
  mean time:        134.412 ns (11.07% GC)
  maximum time:     3.476 μs (95.05% GC)
  --------------
  samples:          10000
  evals/sample:     927

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     66.424 ns (0.00% GC)
  median time:      66.516 ns (0.00% GC)
  mean time:        67.570 ns (0.00% GC)
  maximum time:     158.024 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     978

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     71.295 ns (0.00% GC)
  median time:      71.364 ns (0.00% GC)
  mean time:        72.025 ns (0.00% GC)
  maximum time:     142.953 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     974

julia> f(df,x) = (df[1]=sin(x); df[2]=cos(x); df);
f (generic function with 2 methods)

julia> x = 2π * rand();

julia> fx = fill(0.0,2);

julia> f(fx,x);

julia> df = fill(0.0,2);

julia> df_ref = [cos(x), -sin(x)];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> complex_cache = DiffEqDiffTools.GradientCache(df,x,Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward}, eltype($x), Val{true}, $fx)
BenchmarkTools.Trial: 
  memory estimate:  320 bytes
  allocs estimate:  4
  --------------
  minimum time:     103.084 ns (0.00% GC)
  median time:      109.442 ns (0.00% GC)
  mean time:        174.353 ns (32.60% GC)
  maximum time:     5.086 μs (96.10% GC)
  --------------
  samples:          10000
  evals/sample:     944

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central}, eltype($x), Val{true}, $fx)
BenchmarkTools.Trial: 
  memory estimate:  320 bytes
  allocs estimate:  4
  --------------
  minimum time:     120.647 ns (0.00% GC)
  median time:      127.123 ns (0.00% GC)
  mean time:        192.883 ns (30.15% GC)
  maximum time:     5.339 μs (96.36% GC)
  --------------
  samples:          10000
  evals/sample:     927

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:complex}, eltype($x), Val{true}, $fx)
BenchmarkTools.Trial: 
  memory estimate:  224 bytes
  allocs estimate:  3
  --------------
  minimum time:     107.841 ns (0.00% GC)
  median time:      115.693 ns (0.00% GC)
  mean time:        162.438 ns (26.06% GC)
  maximum time:     5.476 μs (96.71% GC)
  --------------
  samples:          10000
  evals/sample:     932

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  224 bytes
  allocs estimate:  3
  --------------
  minimum time:     85.324 ns (0.00% GC)
  median time:      93.465 ns (0.00% GC)
  mean time:        141.273 ns (30.16% GC)
  maximum time:     5.265 μs (96.61% GC)
  --------------
  samples:          10000
  evals/sample:     964

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  224 bytes
  allocs estimate:  3
  --------------
  minimum time:     101.199 ns (0.00% GC)
  median time:      107.038 ns (0.00% GC)
  mean time:        155.411 ns (27.31% GC)
  maximum time:     5.177 μs (96.17% GC)
  --------------
  samples:          10000
  evals/sample:     951

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  128 bytes
  allocs estimate:  2
  --------------
  minimum time:     86.390 ns (0.00% GC)
  median time:      89.082 ns (0.00% GC)
  mean time:        105.994 ns (14.08% GC)
  maximum time:     3.261 μs (95.69% GC)
  --------------
  samples:          10000
  evals/sample:     957

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     46.069 ns (0.00% GC)
  median time:      46.254 ns (0.00% GC)
  mean time:        46.651 ns (0.00% GC)
  maximum time:     100.605 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     990

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     58.925 ns (0.00% GC)
  median time:      59.052 ns (0.00% GC)
  mean time:        59.880 ns (0.00% GC)
  maximum time:     130.119 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     984

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     57.882 ns (0.00% GC)
  median time:      58.509 ns (0.00% GC)
  mean time:        58.801 ns (0.00% GC)
  maximum time:     109.049 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     982

julia> f(df,x) = (df[1]=sin(x); df[2]=cos(x); df)
f (generic function with 2 methods)

julia> x = (2π * rand()) * (1 + im);

julia> fx = fill(zero(typeof(x)), 2);

julia> f(fx,x);

julia> df = zero(fx);

julia> df_ref = [cos(x), -sin(x)];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward}, eltype($x), Val{true}, fx)
BenchmarkTools.Trial: 
  memory estimate:  432 bytes
  allocs estimate:  6
  --------------
  minimum time:     923.806 ns (0.00% GC)
  median time:      938.806 ns (0.00% GC)
  mean time:        1.011 μs (5.13% GC)
  maximum time:     107.207 μs (97.12% GC)
  --------------
  samples:          10000
  evals/sample:     31

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central}, eltype($x), Val{true}, fx)
BenchmarkTools.Trial: 
  memory estimate:  432 bytes
  allocs estimate:  6
  --------------
  minimum time:     982.353 ns (0.00% GC)
  median time:      1.006 μs (0.00% GC)
  mean time:        1.092 μs (5.29% GC)
  maximum time:     200.008 μs (97.44% GC)
  --------------
  samples:          10000
  evals/sample:     17

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  256 bytes
  allocs estimate:  3
  --------------
  minimum time:     213.819 ns (0.00% GC)
  median time:      218.867 ns (0.00% GC)
  mean time:        254.395 ns (13.08% GC)
  maximum time:     6.450 μs (94.69% GC)
  --------------
  samples:          10000
  evals/sample:     520

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  256 bytes
  allocs estimate:  3
  --------------
  minimum time:     222.890 ns (0.00% GC)
  median time:      227.662 ns (0.00% GC)
  mean time:        261.552 ns (12.00% GC)
  maximum time:     7.332 μs (95.09% GC)
  --------------
  samples:          10000
  evals/sample:     473

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     168.448 ns (0.00% GC)
  median time:      168.935 ns (0.00% GC)
  mean time:        169.314 ns (0.00% GC)
  maximum time:     273.262 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     728

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     177.623 ns (0.00% GC)
  median time:      178.038 ns (0.00% GC)
  mean time:        178.444 ns (0.00% GC)
  maximum time:     284.457 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     690

julia> # Jacobian tests
       function f(fvec,x)
           fvec[1] = (x[1]+3)*(x[2]^3-7)+18
           fvec[2] = sin(x[2]*exp(x[1])-1)
       end
f (generic function with 2 methods)

julia> x = rand(2); y = rand(2);

julia> f(y,x);

julia> J_ref = [[-7+x[2]^3 3*(3+x[1])*x[2]^2]; [exp(x[1])*x[2]*cos(1-exp(x[1])*x[2]) exp(x[1])*cos(1-exp(x[1])*x[2])]];

julia> J = zero(J_ref);

julia> df = zero(x);

julia> df_ref = diag(J_ref);

julia> epsilon = zero(x);

julia> forward_cache = DiffEqDiffTools.JacobianCache(x,Val{:forward});

julia> central_cache = DiffEqDiffTools.JacobianCache(x);

julia> complex_cache = DiffEqDiffTools.JacobianCache(x,Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  528 bytes
  allocs estimate:  7
  --------------
  minimum time:     311.811 ns (0.00% GC)
  median time:      337.037 ns (0.00% GC)
  mean time:        453.229 ns (21.82% GC)
  maximum time:     19.876 μs (97.07% GC)
  --------------
  samples:          10000
  evals/sample:     243

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     219.346 ns (0.00% GC)
  median time:      220.312 ns (0.00% GC)
  mean time:        236.183 ns (5.04% GC)
  maximum time:     5.427 μs (93.95% GC)
  --------------
  samples:          10000
  evals/sample:     506

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x)
BenchmarkTools.Trial: 
  memory estimate:  432 bytes
  allocs estimate:  5
  --------------
  minimum time:     282.537 ns (0.00% GC)
  median time:      303.678 ns (0.00% GC)
  mean time:        400.938 ns (20.98% GC)
  maximum time:     17.542 μs (96.98% GC)
  --------------
  samples:          10000
  evals/sample:     287

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     205.708 ns (0.00% GC)
  median time:      206.951 ns (0.00% GC)
  mean time:        222.200 ns (5.33% GC)
  maximum time:     4.443 μs (92.46% GC)
  --------------
  samples:          10000
  evals/sample:     651

julia> function f(fvec,x)
           fvec[1] = (im*x[1]+3)*(x[2]^3-7)+18
           fvec[2] = sin(x[2]*exp(x[1])-1)
       end
f (generic function with 2 methods)

julia> x = rand(2) + im*rand(2);

julia> y = similar(x);

julia> f(y,x);

julia> J_ref = [[im*(-7+x[2]^3) 3*(3+im*x[1])*x[2]^2]; [exp(x[1])*x[2]*cos(1-exp(x[1])*x[2]) exp(x[1])*cos(1-exp(x[1])*x[2])]];

julia> J = zero(J_ref);

julia> df = zero(x);

julia> df_ref = diag(J_ref);

julia> epsilon = zero(real.(x));

julia> forward_cache = DiffEqDiffTools.JacobianCache(x,Val{:forward});

julia> central_cache = DiffEqDiffTools.JacobianCache(x);

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  624 bytes
  allocs estimate:  7
  --------------
  minimum time:     568.811 ns (0.00% GC)
  median time:      590.700 ns (0.00% GC)
  mean time:        674.430 ns (11.33% GC)
  maximum time:     17.192 μs (94.70% GC)
  --------------
  samples:          10000
  evals/sample:     185

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  144 bytes
  allocs estimate:  1
  --------------
  minimum time:     446.939 ns (0.00% GC)
  median time:      452.182 ns (0.00% GC)
  mean time:        471.245 ns (3.04% GC)
  maximum time:     13.304 μs (94.48% GC)
  --------------
  samples:          10000
  evals/sample:     198

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x)
BenchmarkTools.Trial: 
  memory estimate:  512 bytes
  allocs estimate:  5
  --------------
  minimum time:     525.779 ns (0.00% GC)
  median time:      542.042 ns (0.00% GC)
  mean time:        612.904 ns (10.47% GC)
  maximum time:     17.123 μs (94.41% GC)
  --------------
  samples:          10000
  evals/sample:     190

With this PR (note that the old interface support works by calling the new interface):

julia> using DiffEqDiffTools

julia> using Compat, Compat.LinearAlgebra

julia> using BenchmarkTools

julia> # TODO: add tests for GPUArrays
       # TODO: add tests for DEDataArrays

       # Derivative tests
       x = collect(Compat.range(-2π, stop=2π, length=100));

julia> y = sin.(x);

julia> df = fill(0.0,100);

julia> epsilon = fill(0.0,100);

julia> df_ref = cos.(x);

julia> forward_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:forward});

julia> central_cache = DiffEqDiffTools.DerivativeCache(x, nothing, epsilon, Val{:central});

julia> complex_cache = DiffEqDiffTools.DerivativeCache(x, nothing, nothing, Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, π/4, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     20.403 ns (0.00% GC)
  median time:      20.468 ns (0.00% GC)
  mean time:        20.787 ns (0.00% GC)
  maximum time:     50.594 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, π/4, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     31.158 ns (0.00% GC)
  median time:      31.232 ns (0.00% GC)
  mean time:        32.226 ns (0.00% GC)
  maximum time:     77.138 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     994

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, π/4, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     26.824 ns (0.00% GC)
  median time:      26.980 ns (0.00% GC)
  mean time:        27.273 ns (0.00% GC)
  maximum time:     74.724 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     995

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     1.961 μs (0.00% GC)
  median time:      2.078 μs (0.00% GC)
  mean time:        2.365 μs (3.12% GC)
  maximum time:     123.183 μs (95.22% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.049 μs (0.00% GC)
  median time:      2.127 μs (0.00% GC)
  mean time:        2.444 μs (3.43% GC)
  maximum time:     148.312 μs (95.30% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.749 μs (0.00% GC)
  median time:      2.840 μs (0.00% GC)
  mean time:        3.023 μs (2.49% GC)
  maximum time:     130.024 μs (94.21% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  928 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.330 μs (0.00% GC)
  median time:      1.372 μs (0.00% GC)
  mean time:        1.531 μs (2.82% GC)
  maximum time:     115.826 μs (95.34% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:central}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.043 μs (0.00% GC)
  median time:      2.080 μs (0.00% GC)
  mean time:        2.240 μs (3.17% GC)
  maximum time:     111.328 μs (94.50% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:complex}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.763 μs (0.00% GC)
  median time:      2.860 μs (0.00% GC)
  mean time:        3.056 μs (2.50% GC)
  maximum time:     131.964 μs (94.66% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.876 μs (0.00% GC)
  median time:      1.905 μs (0.00% GC)
  mean time:        2.074 μs (2.00% GC)
  maximum time:     115.644 μs (94.58% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.968 μs (0.00% GC)
  median time:      2.000 μs (0.00% GC)
  mean time:        2.259 μs (2.01% GC)
  maximum time:     124.269 μs (95.14% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     2.667 μs (0.00% GC)
  median time:      2.704 μs (0.00% GC)
  mean time:        2.955 μs (1.30% GC)
  maximum time:     157.043 μs (94.83% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  32 bytes
  allocs estimate:  1
  --------------
  minimum time:     1.273 μs (0.00% GC)
  median time:      1.282 μs (0.00% GC)
  mean time:        1.337 μs (0.00% GC)
  maximum time:     4.879 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:central}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.966 μs (0.00% GC)
  median time:      1.992 μs (0.00% GC)
  mean time:        2.158 μs (1.93% GC)
  maximum time:     114.216 μs (94.89% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:complex}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     2.676 μs (0.00% GC)
  median time:      2.761 μs (0.00% GC)
  mean time:        3.045 μs (1.22% GC)
  maximum time:     134.321 μs (94.35% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     1.049 μs (0.00% GC)
  median time:      1.054 μs (0.00% GC)
  mean time:        1.077 μs (0.00% GC)
  maximum time:     4.006 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     1.914 μs (0.00% GC)
  median time:      1.921 μs (0.00% GC)
  mean time:        1.973 μs (0.00% GC)
  maximum time:     5.239 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     2.614 μs (0.00% GC)
  median time:      2.624 μs (0.00% GC)
  mean time:        2.703 μs (0.00% GC)
  maximum time:     7.982 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> x = x + im*x;

julia> f(x) = sin(x) + cos(x);

julia> y = f.(x);

julia> df = zero(x);

julia> epsilon = similar(real(x));

julia> df_ref = cos.(x) - sin.(x);

julia> forward_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:forward});

julia> central_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:central});
Warning: Pre-computed function values are only useful for fdtype==Val(:forward).

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4+im*π/4, Val{:forward}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     155.578 ns (0.00% GC)
  median time:      155.800 ns (0.00% GC)
  mean time:        158.158 ns (0.00% GC)
  maximum time:     280.940 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     803

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4+im*π/4, Val{:central}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     165.907 ns (0.00% GC)
  median time:      166.468 ns (0.00% GC)
  mean time:        167.338 ns (0.00% GC)
  maximum time:     293.889 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     763

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     16.085 μs (0.00% GC)
  median time:      16.284 μs (0.00% GC)
  mean time:        17.152 μs (0.76% GC)
  maximum time:     1.369 ms (94.89% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     16.193 μs (0.00% GC)
  median time:      16.401 μs (0.00% GC)
  mean time:        17.208 μs (0.70% GC)
  maximum time:     1.277 ms (94.95% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, eltype($x), $y)
BenchmarkTools.Trial: 
  memory estimate:  2.67 KiB
  allocs estimate:  3
  --------------
  minimum time:     9.165 μs (0.00% GC)
  median time:      9.352 μs (0.00% GC)
  mean time:        9.890 μs (0.00% GC)
  maximum time:     34.823 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:central}, eltype($x))#, $y)
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     16.266 μs (0.00% GC)
  median time:      16.418 μs (0.00% GC)
  mean time:        17.167 μs (0.72% GC)
  maximum time:     1.308 ms (95.02% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  1.80 KiB
  allocs estimate:  2
  --------------
  minimum time:     9.063 μs (0.00% GC)
  median time:      9.194 μs (0.00% GC)
  mean time:        9.686 μs (0.00% GC)
  maximum time:     40.094 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     15.973 μs (0.00% GC)
  median time:      16.050 μs (0.00% GC)
  mean time:        16.461 μs (0.00% GC)
  maximum time:     49.518 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:central}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     16.064 μs (0.00% GC)
  median time:      16.131 μs (0.00% GC)
  mean time:        16.407 μs (0.00% GC)
  maximum time:     34.147 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, eltype($x), $y)
BenchmarkTools.Trial: 
  memory estimate:  928 bytes
  allocs estimate:  2
  --------------
  minimum time:     9.025 μs (0.00% GC)
  median time:      9.092 μs (0.00% GC)
  mean time:        9.430 μs (0.00% GC)
  maximum time:     32.837 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  32 bytes
  allocs estimate:  1
  --------------
  minimum time:     8.941 μs (0.00% GC)
  median time:      8.978 μs (0.00% GC)
  mean time:        8.994 μs (0.00% GC)
  maximum time:     19.511 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     3

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     8.903 μs (0.00% GC)
  median time:      8.930 μs (0.00% GC)
  mean time:        8.945 μs (0.00% GC)
  maximum time:     21.639 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     3

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     15.948 μs (0.00% GC)
  median time:      15.998 μs (0.00% GC)
  mean time:        16.036 μs (0.00% GC)
  maximum time:     33.155 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> x = collect(Compat.range(-2π, stop=2π, length=100));

julia> f(x) = sin(x) + im*cos(x);

julia> y = f.(x);

julia> df = fill(zero(Complex{eltype(x)}), size(x));

julia> epsilon = similar(real(x));

julia> df_ref = cos.(x) - im*sin.(x);

julia> forward_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:forward}, eltype(df));

julia> central_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:central}, eltype(df));
Warning: Pre-computed function values are only useful for fdtype==Val(:forward).

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4, Val{:forward}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     42.361 ns (0.00% GC)
  median time:      43.520 ns (0.00% GC)
  mean time:        44.484 ns (0.00% GC)
  maximum time:     127.542 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     990

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4, Val{:central}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     52.025 ns (0.00% GC)
  median time:      52.245 ns (0.00% GC)
  mean time:        53.730 ns (0.00% GC)
  maximum time:     138.504 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     985

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     4.194 μs (0.00% GC)
  median time:      4.262 μs (0.00% GC)
  mean time:        4.495 μs (2.23% GC)
  maximum time:     159.383 μs (93.94% GC)
  --------------
  samples:          10000
  evals/sample:     7

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:central}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     3.943 μs (0.00% GC)
  median time:      4.030 μs (0.00% GC)
  mean time:        4.352 μs (2.84% GC)
  maximum time:     205.610 μs (93.88% GC)
  --------------
  samples:          10000
  evals/sample:     8

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, Complex{eltype($x)}, $y)
BenchmarkTools.Trial: 
  memory estimate:  2.67 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.679 μs (0.00% GC)
  median time:      2.777 μs (0.00% GC)
  mean time:        3.039 μs (3.92% GC)
  maximum time:     155.579 μs (93.67% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, Complex{eltype($x)}, $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  1.80 KiB
  allocs estimate:  2
  --------------
  minimum time:     2.625 μs (0.00% GC)
  median time:      2.695 μs (0.00% GC)
  mean time:        2.936 μs (2.84% GC)
  maximum time:     127.820 μs (94.59% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     4.067 μs (0.00% GC)
  median time:      4.229 μs (0.00% GC)
  mean time:        4.764 μs (0.79% GC)
  maximum time:     224.906 μs (94.13% GC)
  --------------
  samples:          10000
  evals/sample:     7

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:central}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     3.832 μs (0.00% GC)
  median time:      3.874 μs (0.00% GC)
  mean time:        4.037 μs (1.05% GC)
  maximum time:     151.666 μs (94.04% GC)
  --------------
  samples:          10000
  evals/sample:     8

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, Complex{eltype($x)}, $y)
BenchmarkTools.Trial: 
  memory estimate:  928 bytes
  allocs estimate:  2
  --------------
  minimum time:     2.570 μs (0.00% GC)
  median time:      2.602 μs (0.00% GC)
  mean time:        2.724 μs (1.40% GC)
  maximum time:     140.353 μs (94.92% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, Complex{eltype($x)}, $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  32 bytes
  allocs estimate:  1
  --------------
  minimum time:     2.513 μs (0.00% GC)
  median time:      2.533 μs (0.00% GC)
  mean time:        2.573 μs (0.00% GC)
  maximum time:     6.655 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     2.290 μs (0.00% GC)
  median time:      2.298 μs (0.00% GC)
  mean time:        2.325 μs (0.00% GC)
  maximum time:     5.892 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     3.772 μs (0.00% GC)
  median time:      3.782 μs (0.00% GC)
  mean time:        3.804 μs (0.00% GC)
  maximum time:     8.062 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     8

julia> # Gradient tests
       f(x) = 2x[1] + x[2]^2;

julia> x = rand(2);

julia> fx = f(x);

julia> df = fill(0.0, 2);

julia> df_ref = [2., 2*x[2]];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> complex_cache = DiffEqDiffTools.GradientCache(df,x,Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  96 bytes
  allocs estimate:  1
  --------------
  minimum time:     285.155 ns (0.00% GC)
  median time:      288.524 ns (0.00% GC)
  mean time:        301.582 ns (2.58% GC)
  maximum time:     8.446 μs (94.19% GC)
  --------------
  samples:          10000
  evals/sample:     271

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  96 bytes
  allocs estimate:  1
  --------------
  minimum time:     340.794 ns (0.00% GC)
  median time:      343.413 ns (0.00% GC)
  mean time:        359.848 ns (2.48% GC)
  maximum time:     10.889 μs (94.53% GC)
  --------------
  samples:          10000
  evals/sample:     218

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  224 bytes
  allocs estimate:  3
  --------------
  minimum time:     369.694 ns (0.00% GC)
  median time:      375.175 ns (0.00% GC)
  mean time:        411.645 ns (6.06% GC)
  maximum time:     13.509 μs (94.62% GC)
  --------------
  samples:          10000
  evals/sample:     206

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     267.587 ns (0.00% GC)
  median time:      269.497 ns (0.00% GC)
  mean time:        272.911 ns (0.00% GC)
  maximum time:     751.300 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     310

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     322.451 ns (0.00% GC)
  median time:      323.751 ns (0.00% GC)
  mean time:        328.743 ns (0.00% GC)
  maximum time:     715.249 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     233

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  128 bytes
  allocs estimate:  2
  --------------
  minimum time:     349.230 ns (0.00% GC)
  median time:      355.451 ns (0.00% GC)
  mean time:        370.584 ns (2.08% GC)
  maximum time:     7.866 μs (92.27% GC)
  --------------
  samples:          10000
  evals/sample:     213

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     10.928 ns (0.00% GC)
  median time:      11.338 ns (0.00% GC)
  mean time:        11.391 ns (0.00% GC)
  maximum time:     37.322 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     999

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     19.596 ns (0.00% GC)
  median time:      19.744 ns (0.00% GC)
  mean time:        19.967 ns (0.00% GC)
  maximum time:     49.438 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     21.322 ns (0.00% GC)
  median time:      21.335 ns (0.00% GC)
  mean time:        21.615 ns (0.00% GC)
  maximum time:     54.620 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> f(x) = 2x[1] + im*2x[1] + x[2]^2;

julia> x = x + im*x;

julia> fx = f(x);

julia> df = zero(x);

julia> df_ref = conj([2.0+2.0*im, 2.0*x[2]]);

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     151.603 ns (0.00% GC)
  median time:      153.106 ns (0.00% GC)
  mean time:        162.460 ns (3.70% GC)
  maximum time:     1.799 μs (88.69% GC)
  --------------
  samples:          10000
  evals/sample:     836

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     157.612 ns (0.00% GC)
  median time:      158.683 ns (0.00% GC)
  mean time:        168.262 ns (3.68% GC)
  maximum time:     1.931 μs (89.11% GC)
  --------------
  samples:          10000
  evals/sample:     818

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     121.313 ns (0.00% GC)
  median time:      121.506 ns (0.00% GC)
  mean time:        124.875 ns (0.00% GC)
  maximum time:     235.410 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     903

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     129.463 ns (0.00% GC)
  median time:      129.796 ns (0.00% GC)
  mean time:        131.708 ns (0.00% GC)
  maximum time:     249.424 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     883

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     117.967 ns (0.00% GC)
  median time:      118.070 ns (0.00% GC)
  mean time:        119.747 ns (0.00% GC)
  maximum time:     230.232 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     909

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     126.651 ns (0.00% GC)
  median time:      126.743 ns (0.00% GC)
  mean time:        128.730 ns (0.00% GC)
  maximum time:     264.912 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     891

julia> f(x) = sum(abs2, x);

julia> x = ones(2) * (1 + im);

julia> fx = f(x);

julia> df = zero(x);

julia> df_ref = 2*x;

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     119.780 ns (0.00% GC)
  median time:      121.491 ns (0.00% GC)
  mean time:        130.684 ns (4.78% GC)
  maximum time:     1.795 μs (89.90% GC)
  --------------
  samples:          10000
  evals/sample:     921

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     106.245 ns (0.00% GC)
  median time:      107.475 ns (0.00% GC)
  mean time:        116.522 ns (5.37% GC)
  maximum time:     1.685 μs (91.02% GC)
  --------------
  samples:          10000
  evals/sample:     944

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     85.410 ns (0.00% GC)
  median time:      85.808 ns (0.00% GC)
  mean time:        87.238 ns (0.00% GC)
  maximum time:     177.681 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     963

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     78.284 ns (0.00% GC)
  median time:      79.652 ns (0.00% GC)
  mean time:        80.350 ns (0.00% GC)
  maximum time:     170.520 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     968

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     83.383 ns (0.00% GC)
  median time:      83.929 ns (0.00% GC)
  mean time:        84.783 ns (0.00% GC)
  maximum time:     155.742 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     963

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     75.797 ns (0.00% GC)
  median time:      76.275 ns (0.00% GC)
  mean time:        77.043 ns (0.00% GC)
  maximum time:     138.497 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     971

julia> f(x) = 2*x[1] + im*x[2]^2;

julia> x = ones(2);

julia> fx = f(x);

julia> df = fill(zero(eltype(fx)), size(x));

julia> df_ref = [2.0, -im*2*x[2]];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward},eltype(df));

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central},eltype(df));

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  240 bytes
  allocs estimate:  3
  --------------
  minimum time:     117.947 ns (0.00% GC)
  median time:      123.060 ns (0.00% GC)
  mean time:        141.379 ns (11.06% GC)
  maximum time:     1.995 μs (91.26% GC)
  --------------
  samples:          10000
  evals/sample:     918

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  240 bytes
  allocs estimate:  3
  --------------
  minimum time:     120.101 ns (0.00% GC)
  median time:      126.915 ns (0.00% GC)
  mean time:        145.266 ns (10.72% GC)
  maximum time:     2.287 μs (87.79% GC)
  --------------
  samples:          10000
  evals/sample:     911

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  128 bytes
  allocs estimate:  2
  --------------
  minimum time:     97.433 ns (0.00% GC)
  median time:      100.884 ns (0.00% GC)
  mean time:        111.612 ns (7.75% GC)
  maximum time:     1.931 μs (92.07% GC)
  --------------
  samples:          10000
  evals/sample:     949

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  128 bytes
  allocs estimate:  2
  --------------
  minimum time:     100.213 ns (0.00% GC)
  median time:      103.704 ns (0.00% GC)
  mean time:        114.938 ns (7.52% GC)
  maximum time:     2.501 μs (89.20% GC)
  --------------
  samples:          10000
  evals/sample:     943

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     67.520 ns (0.00% GC)
  median time:      67.980 ns (0.00% GC)
  mean time:        69.841 ns (0.00% GC)
  maximum time:     156.661 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     973

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     72.024 ns (0.00% GC)
  median time:      72.534 ns (0.00% GC)
  mean time:        73.844 ns (0.00% GC)
  maximum time:     186.357 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     973

julia> f(df,x) = (df[1]=sin(x); df[2]=cos(x); df);

julia> x = 2π * rand();

julia> fx = fill(0.0,2);

julia> f(fx,x);

julia> df = fill(0.0,2);

julia> df_ref = [cos(x), -sin(x)];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> complex_cache = DiffEqDiffTools.GradientCache(df,x,Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward}, eltype($x), Val{true}, $fx)
BenchmarkTools.Trial: 
  memory estimate:  352 bytes
  allocs estimate:  6
  --------------
  minimum time:     539.212 ns (0.00% GC)
  median time:      552.429 ns (0.00% GC)
  mean time:        610.339 ns (6.84% GC)
  maximum time:     17.113 μs (94.88% GC)
  --------------
  samples:          10000
  evals/sample:     189

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central}, eltype($x), Val{true}, $fx)
BenchmarkTools.Trial: 
  memory estimate:  352 bytes
  allocs estimate:  6
  --------------
  minimum time:     601.797 ns (0.00% GC)
  median time:      614.189 ns (0.00% GC)
  mean time:        674.833 ns (6.16% GC)
  maximum time:     18.105 μs (94.80% GC)
  --------------
  samples:          10000
  evals/sample:     177

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:complex}, eltype($x), Val{true}, $fx)
BenchmarkTools.Trial: 
  memory estimate:  256 bytes
  allocs estimate:  5
  --------------
  minimum time:     569.630 ns (0.00% GC)
  median time:      578.014 ns (0.00% GC)
  mean time:        628.659 ns (4.72% GC)
  maximum time:     19.141 μs (94.55% GC)
  --------------
  samples:          10000
  evals/sample:     184

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  240 bytes
  allocs estimate:  4
  --------------
  minimum time:     434.919 ns (0.00% GC)
  median time:      442.735 ns (0.00% GC)
  mean time:        483.548 ns (5.84% GC)
  maximum time:     17.524 μs (95.38% GC)
  --------------
  samples:          10000
  evals/sample:     198

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  240 bytes
  allocs estimate:  4
  --------------
  minimum time:     474.286 ns (0.00% GC)
  median time:      480.204 ns (0.00% GC)
  mean time:        524.472 ns (5.41% GC)
  maximum time:     15.462 μs (93.78% GC)
  --------------
  samples:          10000
  evals/sample:     196

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  144 bytes
  allocs estimate:  3
  --------------
  minimum time:     408.150 ns (0.00% GC)
  median time:      411.480 ns (0.00% GC)
  mean time:        430.371 ns (2.25% GC)
  maximum time:     9.271 μs (91.40% GC)
  --------------
  samples:          10000
  evals/sample:     200

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     46.408 ns (0.00% GC)
  median time:      46.820 ns (0.00% GC)
  mean time:        47.385 ns (0.00% GC)
  maximum time:     110.633 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     989

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     56.115 ns (0.00% GC)
  median time:      56.221 ns (0.00% GC)
  mean time:        57.107 ns (0.00% GC)
  maximum time:     120.697 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     984

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     60.294 ns (0.00% GC)
  median time:      60.744 ns (0.00% GC)
  mean time:        61.572 ns (0.00% GC)
  maximum time:     135.082 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     982

julia> f(df,x) = (df[1]=sin(x); df[2]=cos(x); df);

julia> x = (2π * rand()) * (1 + im);

julia> fx = fill(zero(typeof(x)), 2);

julia> f(fx,x);

julia> df = zero(fx);

julia> df_ref = [cos(x), -sin(x)];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward}, eltype($x), Val{true}, fx)
BenchmarkTools.Trial: 
  memory estimate:  496 bytes
  allocs estimate:  8
  --------------
  minimum time:     1.211 μs (0.00% GC)
  median time:      1.239 μs (0.00% GC)
  mean time:        1.341 μs (3.37% GC)
  maximum time:     233.187 μs (97.04% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central}, eltype($x), Val{true}, fx)
BenchmarkTools.Trial: 
  memory estimate:  496 bytes
  allocs estimate:  8
  --------------
  minimum time:     1.389 μs (0.00% GC)
  median time:      1.425 μs (0.00% GC)
  mean time:        1.516 μs (2.99% GC)
  maximum time:     235.493 μs (96.88% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  288 bytes
  allocs estimate:  4
  --------------
  minimum time:     670.824 ns (0.00% GC)
  median time:      675.868 ns (0.00% GC)
  mean time:        706.120 ns (3.31% GC)
  maximum time:     13.611 μs (91.55% GC)
  --------------
  samples:          10000
  evals/sample:     159

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  288 bytes
  allocs estimate:  4
  --------------
  minimum time:     714.188 ns (0.00% GC)
  median time:      720.268 ns (0.00% GC)
  mean time:        752.998 ns (3.21% GC)
  maximum time:     15.930 μs (92.83% GC)
  --------------
  samples:          10000
  evals/sample:     138

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     165.603 ns (0.00% GC)
  median time:      166.323 ns (0.00% GC)
  mean time:        166.643 ns (0.00% GC)
  maximum time:     287.307 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     759

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     175.458 ns (0.00% GC)
  median time:      176.332 ns (0.00% GC)
  mean time:        176.633 ns (0.00% GC)
  maximum time:     283.021 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     719

julia> # Jacobian tests
       function f(fvec,x)
           fvec[1] = (x[1]+3)*(x[2]^3-7)+18
           fvec[2] = sin(x[2]*exp(x[1])-1)
       end
f (generic function with 2 methods)

julia> x = rand(2); y = rand(2);

julia> f(y,x);

julia> J_ref = [[-7+x[2]^3 3*(3+x[1])*x[2]^2]; [exp(x[1])*x[2]*cos(1-exp(x[1])*x[2]) exp(x[1])*cos(1-exp(x[1])*x[2])]];

julia> J = zero(J_ref);

julia> df = zero(x);

julia> df_ref = diag(J_ref);

julia> epsilon = zero(x);

julia> forward_cache = DiffEqDiffTools.JacobianCache(x,Val{:forward});

julia> central_cache = DiffEqDiffTools.JacobianCache(x);

julia> complex_cache = DiffEqDiffTools.JacobianCache(x,Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  528 bytes
  allocs estimate:  7
  --------------
  minimum time:     291.288 ns (0.00% GC)
  median time:      315.974 ns (0.00% GC)
  mean time:        387.741 ns (16.02% GC)
  maximum time:     12.321 μs (95.14% GC)
  --------------
  samples:          10000
  evals/sample:     267

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  176 bytes
  allocs estimate:  5
  --------------
  minimum time:     234.649 ns (0.00% GC)
  median time:      239.047 ns (0.00% GC)
  mean time:        256.158 ns (4.90% GC)
  maximum time:     4.905 μs (92.67% GC)
  --------------
  samples:          10000
  evals/sample:     405

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x)
BenchmarkTools.Trial: 
  memory estimate:  496 bytes
  allocs estimate:  9
  --------------
  minimum time:     928.314 ns (0.00% GC)
  median time:      946.000 ns (0.00% GC)
  mean time:        1.036 μs (6.01% GC)
  maximum time:     102.818 μs (96.65% GC)
  --------------
  samples:          10000
  evals/sample:     35

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  176 bytes
  allocs estimate:  3
  --------------
  minimum time:     195.309 ns (0.00% GC)
  median time:      200.304 ns (0.00% GC)
  mean time:        221.285 ns (7.42% GC)
  maximum time:     4.013 μs (92.87% GC)
  --------------
  samples:          10000
  evals/sample:     606

julia> function f(fvec,x)
           fvec[1] = (im*x[1]+3)*(x[2]^3-7)+18
           fvec[2] = sin(x[2]*exp(x[1])-1)
       end
f (generic function with 2 methods)

julia> x = rand(2) + im*rand(2);

julia> y = similar(x);

julia> f(y,x);

julia> J_ref = [[im*(-7+x[2]^3) 3*(3+im*x[1])*x[2]^2]; [exp(x[1])*x[2]*cos(1-exp(x[1])*x[2]) exp(x[1])*cos(1-exp(x[1])*x[2])]];

julia> J = zero(J_ref);

julia> df = zero(x);

julia> df_ref = diag(J_ref);

julia> epsilon = zero(real.(x));

julia> forward_cache = DiffEqDiffTools.JacobianCache(x,Val{:forward});

julia> central_cache = DiffEqDiffTools.JacobianCache(x);

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  624 bytes
  allocs estimate:  7
  --------------
  minimum time:     576.077 ns (0.00% GC)
  median time:      598.350 ns (0.00% GC)
  mean time:        647.904 ns (6.67% GC)
  maximum time:     10.242 μs (91.17% GC)
  --------------
  samples:          10000
  evals/sample:     183

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  272 bytes
  allocs estimate:  5
  --------------
  minimum time:     492.928 ns (0.00% GC)
  median time:      499.443 ns (0.00% GC)
  mean time:        533.423 ns (4.57% GC)
  maximum time:     11.857 μs (92.66% GC)
  --------------
  samples:          10000
  evals/sample:     194

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x)
BenchmarkTools.Trial: 
  memory estimate:  640 bytes
  allocs estimate:  9
  --------------
  minimum time:     571.652 ns (0.00% GC)
  median time:      603.772 ns (0.00% GC)
  mean time:        669.417 ns (8.87% GC)
  maximum time:     13.228 μs (91.28% GC)
  --------------
  samples:          10000
  evals/sample:     184

@chriselrod
Copy link
Author

Here comes a similar comment for 0.7. For testing on the current master, I started Julia with --depwarn=no. Depwarnings were on for this PR, because handling them was the point of the PR in the first place.

Oddly, while most things seemed to stay the same, cases where complex numbers are involved seem to be better on this PR.

julia> versioninfo()
Julia Version 0.7.0-DEV.4693
Commit bf1215c* (2018-03-25 14:08 UTC)
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
  WORD_SIZE: 64
  LIBM: libimf
  LLVM: libLLVM-3.9.1 (ORCJIT, haswell)
Environment:
  JULIA_NUM_THREADS = 8

current master:

julia> using DiffEqDiffTools
[ Info: Recompiling stale cache file /home/celrod/.julia/compiled/v0.7/DiffEqDiffTools.ji for module DiffEqDiffTools

julia> using Compat, Compat.LinearAlgebra

julia> using BenchmarkTools

julia> # TODO: add tests for GPUArrays
       # TODO: add tests for DEDataArrays

       # Derivative tests
       x = collect(Compat.range(-2π, stop=2π, length=100));

julia> y = sin.(x);

julia> df = fill(0.0,100);

julia> epsilon = fill(0.0,100);

julia> df_ref = cos.(x);

julia> forward_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:forward});

julia> central_cache = DiffEqDiffTools.DerivativeCache(x, nothing, epsilon, Val{:central});

julia> complex_cache = DiffEqDiffTools.DerivativeCache(x, nothing, nothing, Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, π/4, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     17.305 ns (0.00% GC)
  median time:      17.376 ns (0.00% GC)
  mean time:        17.874 ns (0.00% GC)
  maximum time:     60.894 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, π/4, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     21.316 ns (0.00% GC)
  median time:      21.374 ns (0.00% GC)
  mean time:        21.615 ns (0.00% GC)
  maximum time:     87.351 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, π/4, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     25.007 ns (0.00% GC)
  median time:      25.098 ns (0.00% GC)
  mean time:        25.311 ns (0.00% GC)
  maximum time:     50.066 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     996

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     1.921 μs (0.00% GC)
  median time:      1.957 μs (0.00% GC)
  mean time:        2.587 μs (19.61% GC)
  maximum time:     3.708 ms (99.87% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.018 μs (0.00% GC)
  median time:      2.445 μs (0.00% GC)
  mean time:        3.649 μs (21.11% GC)
  maximum time:     6.052 ms (99.88% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.669 μs (0.00% GC)
  median time:      2.712 μs (0.00% GC)
  mean time:        3.466 μs (16.47% GC)
  maximum time:     4.164 ms (99.86% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  928 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.040 μs (0.00% GC)
  median time:      1.137 μs (0.00% GC)
  mean time:        1.684 μs (29.94% GC)
  maximum time:     3.786 ms (99.92% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:central}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.046 μs (0.00% GC)
  median time:      2.121 μs (0.00% GC)
  mean time:        2.877 μs (20.18% GC)
  maximum time:     4.235 ms (99.89% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:complex}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.681 μs (0.00% GC)
  median time:      2.712 μs (0.00% GC)
  mean time:        3.563 μs (16.50% GC)
  maximum time:     4.291 ms (99.86% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.696 μs (0.00% GC)
  median time:      1.855 μs (0.00% GC)
  mean time:        2.441 μs (21.33% GC)
  maximum time:     3.861 ms (99.90% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.759 μs (0.00% GC)
  median time:      1.914 μs (0.00% GC)
  mean time:        2.540 μs (21.02% GC)
  maximum time:     3.985 ms (99.89% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     2.370 μs (0.00% GC)
  median time:      2.543 μs (0.00% GC)
  mean time:        3.165 μs (17.68% GC)
  maximum time:     4.252 ms (99.85% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  32 bytes
  allocs estimate:  1
  --------------
  minimum time:     1.006 μs (0.00% GC)
  median time:      1.013 μs (0.00% GC)
  mean time:        1.022 μs (0.00% GC)
  maximum time:     2.833 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:central}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.759 μs (0.00% GC)
  median time:      1.909 μs (0.00% GC)
  mean time:        2.476 μs (20.96% GC)
  maximum time:     3.940 ms (99.89% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:complex}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     2.366 μs (0.00% GC)
  median time:      2.538 μs (0.00% GC)
  mean time:        3.185 μs (17.75% GC)
  maximum time:     4.275 ms (99.88% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     1.009 μs (0.00% GC)
  median time:      1.015 μs (0.00% GC)
  mean time:        1.021 μs (0.00% GC)
  maximum time:     3.518 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     1.712 μs (0.00% GC)
  median time:      1.717 μs (0.00% GC)
  mean time:        1.757 μs (0.00% GC)
  maximum time:     6.876 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     2.327 μs (0.00% GC)
  median time:      2.336 μs (0.00% GC)
  mean time:        2.366 μs (0.00% GC)
  maximum time:     6.689 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> x = x + im*x;

julia> f(x) = sin(x) + cos(x);

julia> y = f.(x);

julia> df = zero(x);

julia> epsilon = similar(real(x));

julia> df_ref = cos.(x) - sin.(x);

julia> forward_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:forward});

julia> central_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:central});
WARNING: Pre-computed function values are only useful for fdtype==Val{:forward}.

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4+im*π/4, Val{:forward}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     192.068 ns (0.00% GC)
  median time:      192.753 ns (0.00% GC)
  mean time:        194.405 ns (0.00% GC)
  maximum time:     394.204 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     631

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4+im*π/4, Val{:central}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     186.925 ns (0.00% GC)
  median time:      188.971 ns (0.00% GC)
  mean time:        191.838 ns (0.00% GC)
  maximum time:     433.258 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     656

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     19.652 μs (0.00% GC)
  median time:      20.482 μs (0.00% GC)
  mean time:        21.797 μs (5.26% GC)
  maximum time:     11.519 ms (99.54% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     19.556 μs (0.00% GC)
  median time:      20.406 μs (0.00% GC)
  mean time:        21.847 μs (5.32% GC)
  maximum time:     11.670 ms (99.56% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, eltype($x), $y)
BenchmarkTools.Trial: 
  memory estimate:  2.67 KiB
  allocs estimate:  3
  --------------
  minimum time:     10.663 μs (0.00% GC)
  median time:      11.469 μs (0.00% GC)
  mean time:        11.441 μs (0.00% GC)
  maximum time:     29.495 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:central}, eltype($x))#, $y)
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     19.543 μs (0.00% GC)
  median time:      20.357 μs (0.00% GC)
  mean time:        21.726 μs (5.45% GC)
  maximum time:     11.892 ms (99.52% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  1.80 KiB
  allocs estimate:  2
  --------------
  minimum time:     10.281 μs (0.00% GC)
  median time:      10.380 μs (0.00% GC)
  mean time:        10.844 μs (0.00% GC)
  maximum time:     30.801 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     19.292 μs (0.00% GC)
  median time:      19.381 μs (0.00% GC)
  mean time:        19.686 μs (0.00% GC)
  maximum time:     63.581 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:central}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     19.144 μs (0.00% GC)
  median time:      19.219 μs (0.00% GC)
  mean time:        19.655 μs (0.00% GC)
  maximum time:     59.838 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, eltype($x), $y)
BenchmarkTools.Trial: 
  memory estimate:  928 bytes
  allocs estimate:  2
  --------------
  minimum time:     10.232 μs (0.00% GC)
  median time:      10.284 μs (0.00% GC)
  mean time:        10.570 μs (0.00% GC)
  maximum time:     36.650 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  32 bytes
  allocs estimate:  1
  --------------
  minimum time:     10.177 μs (0.00% GC)
  median time:      10.219 μs (0.00% GC)
  mean time:        10.253 μs (0.00% GC)
  maximum time:     43.101 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     10.184 μs (0.00% GC)
  median time:      10.228 μs (0.00% GC)
  mean time:        10.369 μs (0.00% GC)
  maximum time:     33.621 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     18.983 μs (0.00% GC)
  median time:      19.046 μs (0.00% GC)
  mean time:        19.107 μs (0.00% GC)
  maximum time:     46.169 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> x = collect(Compat.range(-2π, stop=2π, length=100));

julia> f(x) = sin(x) + im*cos(x);

julia> y = f.(x);

julia> df = fill(zero(Complex{eltype(x)}), size(x));

julia> epsilon = similar(real(x));

julia> df_ref = cos.(x) - im*sin.(x);

julia> forward_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:forward}, eltype(df));

julia> central_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:central}, eltype(df));
WARNING: Pre-computed function values are only useful for fdtype==Val{:forward}.

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4, Val{:forward}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     39.547 ns (0.00% GC)
  median time:      39.645 ns (0.00% GC)
  mean time:        40.057 ns (0.00% GC)
  maximum time:     96.852 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     991

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4, Val{:central}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     36.824 ns (0.00% GC)
  median time:      36.886 ns (0.00% GC)
  mean time:        37.164 ns (0.00% GC)
  maximum time:     95.480 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     992

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     3.710 μs (0.00% GC)
  median time:      3.780 μs (0.00% GC)
  mean time:        4.630 μs (14.83% GC)
  maximum time:     4.901 ms (99.85% GC)
  --------------
  samples:          10000
  evals/sample:     8

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:central}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     3.618 μs (0.00% GC)
  median time:      3.684 μs (0.00% GC)
  mean time:        4.551 μs (15.31% GC)
  maximum time:     4.955 ms (99.85% GC)
  --------------
  samples:          10000
  evals/sample:     8

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, Complex{eltype($x)}, $y)
BenchmarkTools.Trial: 
  memory estimate:  2.67 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.239 μs (0.00% GC)
  median time:      2.311 μs (0.00% GC)
  mean time:        3.079 μs (20.28% GC)
  maximum time:     4.405 ms (99.87% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, Complex{eltype($x)}, $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  1.80 KiB
  allocs estimate:  2
  --------------
  minimum time:     1.889 μs (0.00% GC)
  median time:      1.947 μs (0.00% GC)
  mean time:        2.595 μs (21.33% GC)
  maximum time:     3.963 ms (99.89% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     3.353 μs (0.00% GC)
  median time:      3.511 μs (0.00% GC)
  mean time:        4.176 μs (15.65% GC)
  maximum time:     4.958 ms (99.87% GC)
  --------------
  samples:          10000
  evals/sample:     8

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:central}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     3.219 μs (0.00% GC)
  median time:      3.401 μs (0.00% GC)
  mean time:        4.061 μs (16.14% GC)
  maximum time:     4.976 ms (99.87% GC)
  --------------
  samples:          10000
  evals/sample:     8

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, Complex{eltype($x)}, $y)
BenchmarkTools.Trial: 
  memory estimate:  928 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.813 μs (0.00% GC)
  median time:      1.926 μs (0.00% GC)
  mean time:        2.496 μs (21.27% GC)
  maximum time:     3.975 ms (99.89% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, Complex{eltype($x)}, $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  32 bytes
  allocs estimate:  1
  --------------
  minimum time:     1.775 μs (0.00% GC)
  median time:      1.781 μs (0.00% GC)
  mean time:        1.793 μs (0.00% GC)
  maximum time:     3.770 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     1.775 μs (0.00% GC)
  median time:      1.780 μs (0.00% GC)
  mean time:        1.807 μs (0.00% GC)
  maximum time:     5.442 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     3.168 μs (0.00% GC)
  median time:      3.176 μs (0.00% GC)
  mean time:        3.225 μs (0.00% GC)
  maximum time:     8.823 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     8

julia> # Gradient tests
       f(x) = 2x[1] + x[2]^2;

julia> x = rand(2);

julia> fx = f(x);

julia> df = fill(0.0, 2);

julia> df_ref = [2., 2*x[2]];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> complex_cache = DiffEqDiffTools.GradientCache(df,x,Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  96 bytes
  allocs estimate:  1
  --------------
  minimum time:     30.716 ns (0.00% GC)
  median time:      32.734 ns (0.00% GC)
  mean time:        44.138 ns (21.54% GC)
  maximum time:     40.673 μs (99.83% GC)
  --------------
  samples:          10000
  evals/sample:     994

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  96 bytes
  allocs estimate:  1
  --------------
  minimum time:     41.546 ns (0.00% GC)
  median time:      42.938 ns (0.00% GC)
  mean time:        54.353 ns (17.80% GC)
  maximum time:     40.699 μs (99.80% GC)
  --------------
  samples:          10000
  evals/sample:     991

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  384 bytes
  allocs estimate:  4
  --------------
  minimum time:     111.363 ns (0.00% GC)
  median time:      116.582 ns (0.00% GC)
  mean time:        151.246 ns (20.90% GC)
  maximum time:     44.853 μs (99.54% GC)
  --------------
  samples:          10000
  evals/sample:     932

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     11.125 ns (0.00% GC)
  median time:      11.225 ns (0.00% GC)
  mean time:        11.303 ns (0.00% GC)
  maximum time:     47.973 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     999

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     18.881 ns (0.00% GC)
  median time:      18.935 ns (0.00% GC)
  mean time:        19.011 ns (0.00% GC)
  maximum time:     51.476 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  288 bytes
  allocs estimate:  3
  --------------
  minimum time:     93.163 ns (0.00% GC)
  median time:      96.936 ns (0.00% GC)
  mean time:        120.124 ns (17.28% GC)
  maximum time:     42.786 μs (99.65% GC)
  --------------
  samples:          10000
  evals/sample:     952

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     11.104 ns (0.00% GC)
  median time:      11.198 ns (0.00% GC)
  mean time:        11.306 ns (0.00% GC)
  maximum time:     34.653 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     999

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     18.924 ns (0.00% GC)
  median time:      19.025 ns (0.00% GC)
  mean time:        19.314 ns (0.00% GC)
  maximum time:     84.319 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  160 bytes
  allocs estimate:  1
  --------------
  minimum time:     58.841 ns (0.00% GC)
  median time:      61.189 ns (0.00% GC)
  mean time:        72.482 ns (13.20% GC)
  maximum time:     40.819 μs (99.77% GC)
  --------------
  samples:          10000
  evals/sample:     986

julia> f(x) = 2x[1] + im*2x[1] + x[2]^2;

julia> x = x + im*x;

julia> fx = f(x);

julia> df = zero(x);

julia> df_ref = conj([2.0+2.0*im, 2.0*x[2]]);

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     135.913 ns (0.00% GC)
  median time:      137.483 ns (0.00% GC)
  mean time:        152.129 ns (7.37% GC)
  maximum time:     47.011 μs (99.57% GC)
  --------------
  samples:          10000
  evals/sample:     867

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     131.994 ns (0.00% GC)
  median time:      133.529 ns (0.00% GC)
  mean time:        147.776 ns (7.52% GC)
  maximum time:     47.367 μs (99.58% GC)
  --------------
  samples:          10000
  evals/sample:     878

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     116.523 ns (0.00% GC)
  median time:      116.717 ns (0.00% GC)
  mean time:        117.893 ns (0.00% GC)
  maximum time:     245.638 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     916

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     110.206 ns (0.00% GC)
  median time:      110.580 ns (0.00% GC)
  mean time:        111.639 ns (0.00% GC)
  maximum time:     245.340 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     927

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     116.472 ns (0.00% GC)
  median time:      116.653 ns (0.00% GC)
  mean time:        117.367 ns (0.00% GC)
  maximum time:     230.181 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     916

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     110.546 ns (0.00% GC)
  median time:      110.642 ns (0.00% GC)
  mean time:        111.979 ns (0.00% GC)
  maximum time:     223.318 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     927

julia> f(x) = sum(abs2, x);

julia> x = ones(2) * (1 + im);

julia> fx = f(x);

julia> df = zero(x);

julia> df_ref = 2*x;

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     115.185 ns (0.00% GC)
  median time:      117.089 ns (0.00% GC)
  mean time:        131.090 ns (8.50% GC)
  maximum time:     44.750 μs (99.63% GC)
  --------------
  samples:          10000
  evals/sample:     918

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     95.513 ns (0.00% GC)
  median time:      97.886 ns (0.00% GC)
  mean time:        112.728 ns (9.91% GC)
  maximum time:     43.727 μs (99.71% GC)
  --------------
  samples:          10000
  evals/sample:     950

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     88.834 ns (0.00% GC)
  median time:      90.000 ns (0.00% GC)
  mean time:        90.973 ns (0.00% GC)
  maximum time:     195.261 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     957

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     74.616 ns (0.00% GC)
  median time:      74.912 ns (0.00% GC)
  mean time:        75.891 ns (0.00% GC)
  maximum time:     157.132 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     972

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     89.688 ns (0.00% GC)
  median time:      89.902 ns (0.00% GC)
  mean time:        91.058 ns (0.00% GC)
  maximum time:     198.478 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     957

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     74.389 ns (0.00% GC)
  median time:      74.853 ns (0.00% GC)
  mean time:        75.442 ns (0.00% GC)
  maximum time:     153.821 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     972

julia> f(x) = 2*x[1] + im*x[2]^2;

julia> x = ones(2);

julia> fx = f(x);

julia> df = fill(zero(eltype(fx)), size(x));

julia> df_ref = [2.0, -im*2*x[2]];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward},eltype(df));

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central},eltype(df));

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  400 bytes
  allocs estimate:  4
  --------------
  minimum time:     147.318 ns (0.00% GC)
  median time:      152.764 ns (0.00% GC)
  mean time:        186.347 ns (16.02% GC)
  maximum time:     49.869 μs (99.58% GC)
  --------------
  samples:          10000
  evals/sample:     836

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  400 bytes
  allocs estimate:  4
  --------------
  minimum time:     167.148 ns (0.00% GC)
  median time:      172.232 ns (0.00% GC)
  mean time:        207.005 ns (14.89% GC)
  maximum time:     55.032 μs (99.50% GC)
  --------------
  samples:          10000
  evals/sample:     759

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  288 bytes
  allocs estimate:  3
  --------------
  minimum time:     124.993 ns (0.00% GC)
  median time:      128.349 ns (0.00% GC)
  mean time:        155.202 ns (15.33% GC)
  maximum time:     46.120 μs (99.58% GC)
  --------------
  samples:          10000
  evals/sample:     905

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  288 bytes
  allocs estimate:  3
  --------------
  minimum time:     146.122 ns (0.00% GC)
  median time:      149.001 ns (0.00% GC)
  mean time:        177.705 ns (13.75% GC)
  maximum time:     49.768 μs (99.58% GC)
  --------------
  samples:          10000
  evals/sample:     846

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  160 bytes
  allocs estimate:  1
  --------------
  minimum time:     97.521 ns (0.00% GC)
  median time:      100.270 ns (0.00% GC)
  mean time:        113.209 ns (9.56% GC)
  maximum time:     43.975 μs (99.69% GC)
  --------------
  samples:          10000
  evals/sample:     947

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  160 bytes
  allocs estimate:  1
  --------------
  minimum time:     115.654 ns (0.00% GC)
  median time:      118.216 ns (0.00% GC)
  mean time:        133.100 ns (8.29% GC)
  maximum time:     45.306 μs (99.64% GC)
  --------------
  samples:          10000
  evals/sample:     916

julia> f(df,x) = (df[1]=sin(x); df[2]=cos(x); df);

julia> x = 2π * rand();

julia> fx = fill(0.0,2);

julia> f(fx,x);

julia> df = fill(0.0,2);

julia> df_ref = [cos(x), -sin(x)];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> complex_cache = DiffEqDiffTools.GradientCache(df,x,Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward}, eltype($x), Val{true}, $fx)
BenchmarkTools.Trial: 
  memory estimate:  352 bytes
  allocs estimate:  6
  --------------
  minimum time:     177.474 ns (0.00% GC)
  median time:      182.928 ns (0.00% GC)
  mean time:        218.858 ns (14.69% GC)
  maximum time:     58.269 μs (99.53% GC)
  --------------
  samples:          10000
  evals/sample:     732

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central}, eltype($x), Val{true}, $fx)
BenchmarkTools.Trial: 
  memory estimate:  352 bytes
  allocs estimate:  6
  --------------
  minimum time:     217.847 ns (0.00% GC)
  median time:      231.581 ns (0.00% GC)
  mean time:        271.312 ns (13.12% GC)
  maximum time:     80.498 μs (99.59% GC)
  --------------
  samples:          10000
  evals/sample:     530

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:complex}, eltype($x), Val{true}, $fx)
BenchmarkTools.Trial: 
  memory estimate:  256 bytes
  allocs estimate:  5
  --------------
  minimum time:     246.008 ns (0.00% GC)
  median time:      253.485 ns (0.00% GC)
  mean time:        295.976 ns (12.11% GC)
  maximum time:     111.441 μs (99.66% GC)
  --------------
  samples:          10000
  evals/sample:     390

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  224 bytes
  allocs estimate:  3
  --------------
  minimum time:     84.023 ns (0.00% GC)
  median time:      89.611 ns (0.00% GC)
  mean time:        113.719 ns (19.03% GC)
  maximum time:     44.316 μs (99.69% GC)
  --------------
  samples:          10000
  evals/sample:     963

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  224 bytes
  allocs estimate:  3
  --------------
  minimum time:     92.266 ns (0.00% GC)
  median time:      97.673 ns (0.00% GC)
  mean time:        121.564 ns (17.86% GC)
  maximum time:     44.759 μs (99.70% GC)
  --------------
  samples:          10000
  evals/sample:     955

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  128 bytes
  allocs estimate:  2
  --------------
  minimum time:     81.669 ns (0.00% GC)
  median time:      85.401 ns (0.00% GC)
  mean time:        102.391 ns (13.87% GC)
  maximum time:     44.280 μs (99.70% GC)
  --------------
  samples:          10000
  evals/sample:     966

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     38.626 ns (0.00% GC)
  median time:      38.644 ns (0.00% GC)
  mean time:        39.263 ns (0.00% GC)
  maximum time:     102.405 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     992

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     46.782 ns (0.00% GC)
  median time:      46.890 ns (0.00% GC)
  mean time:        47.454 ns (0.00% GC)
  maximum time:     104.129 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     989

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     52.421 ns (0.00% GC)
  median time:      53.293 ns (0.00% GC)
  mean time:        54.163 ns (0.00% GC)
  maximum time:     161.971 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     985

julia> f(df,x) = (df[1]=sin(x); df[2]=cos(x); df);

julia> x = (2π * rand()) * (1 + im);

julia> fx = fill(zero(typeof(x)), 2);

julia> f(fx,x);

julia> df = zero(fx);

julia> df_ref = [cos(x), -sin(x)];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward}, eltype($x), Val{true}, fx)
BenchmarkTools.Trial: 
  memory estimate:  432 bytes
  allocs estimate:  6
  --------------
  minimum time:     647.488 ns (0.00% GC)
  median time:      660.067 ns (0.00% GC)
  mean time:        747.520 ns (8.98% GC)
  maximum time:     263.407 μs (99.65% GC)
  --------------
  samples:          10000
  evals/sample:     164

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central}, eltype($x), Val{true}, fx)
BenchmarkTools.Trial: 
  memory estimate:  432 bytes
  allocs estimate:  6
  --------------
  minimum time:     755.339 ns (0.00% GC)
  median time:      768.508 ns (0.00% GC)
  mean time:        871.006 ns (9.11% GC)
  maximum time:     368.563 μs (99.68% GC)
  --------------
  samples:          10000
  evals/sample:     118

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  256 bytes
  allocs estimate:  3
  --------------
  minimum time:     259.889 ns (0.00% GC)
  median time:      263.483 ns (0.00% GC)
  mean time:        306.835 ns (11.28% GC)
  maximum time:     126.970 μs (99.71% GC)
  --------------
  samples:          10000
  evals/sample:     343

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  256 bytes
  allocs estimate:  3
  --------------
  minimum time:     267.294 ns (0.00% GC)
  median time:      271.947 ns (0.00% GC)
  mean time:        316.329 ns (11.27% GC)
  maximum time:     135.464 μs (99.69% GC)
  --------------
  samples:          10000
  evals/sample:     320

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     211.746 ns (0.00% GC)
  median time:      212.622 ns (0.00% GC)
  mean time:        214.951 ns (0.00% GC)
  maximum time:     470.489 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     540

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     224.561 ns (0.00% GC)
  median time:      225.276 ns (0.00% GC)
  mean time:        229.235 ns (0.00% GC)
  maximum time:     445.274 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     478

julia> # Jacobian tests
       function f(fvec,x)
           fvec[1] = (x[1]+3)*(x[2]^3-7)+18
           fvec[2] = sin(x[2]*exp(x[1])-1)
       end
f (generic function with 2 methods)

julia> x = rand(2); y = rand(2);

julia> f(y,x);

julia> J_ref = [[-7+x[2]^3 3*(3+x[1])*x[2]^2]; [exp(x[1])*x[2]*cos(1-exp(x[1])*x[2]) exp(x[1])*cos(1-exp(x[1])*x[2])]];

julia> J = zero(J_ref);

julia> df = zero(x);

julia> df_ref = diag(J_ref);

julia> epsilon = zero(x);

julia> forward_cache = DiffEqDiffTools.JacobianCache(x,Val{:forward});

julia> central_cache = DiffEqDiffTools.JacobianCache(x);

julia> complex_cache = DiffEqDiffTools.JacobianCache(x,Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  656 bytes
  allocs estimate:  6
  --------------
  minimum time:     285.010 ns (0.00% GC)
  median time:      305.456 ns (0.00% GC)
  mean time:        381.830 ns (18.22% GC)
  maximum time:     146.092 μs (99.66% GC)
  --------------
  samples:          10000
  evals/sample:     298

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  272 bytes
  allocs estimate:  2
  --------------
  minimum time:     172.227 ns (0.00% GC)
  median time:      178.945 ns (0.00% GC)
  mean time:        208.270 ns (12.36% GC)
  maximum time:     58.208 μs (99.57% GC)
  --------------
  samples:          10000
  evals/sample:     750

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x)
BenchmarkTools.Trial: 
  memory estimate:  592 bytes
  allocs estimate:  6
  --------------
  minimum time:     369.500 ns (0.00% GC)
  median time:      391.121 ns (0.00% GC)
  mean time:        477.261 ns (16.57% GC)
  maximum time:     214.631 μs (99.71% GC)
  --------------
  samples:          10000
  evals/sample:     206

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  272 bytes
  allocs estimate:  2
  --------------
  minimum time:     179.358 ns (0.00% GC)
  median time:      184.094 ns (0.00% GC)
  mean time:        213.974 ns (12.12% GC)
  maximum time:     59.416 μs (99.56% GC)
  --------------
  samples:          10000
  evals/sample:     737

julia> function f(fvec,x)
           fvec[1] = (im*x[1]+3)*(x[2]^3-7)+18
           fvec[2] = sin(x[2]*exp(x[1])-1)
       end
f (generic function with 2 methods)

julia> x = rand(2) + im*rand(2);

julia> y = similar(x);

julia> f(y,x);

julia> J_ref = [[im*(-7+x[2]^3) 3*(3+im*x[1])*x[2]^2]; [exp(x[1])*x[2]*cos(1-exp(x[1])*x[2]) exp(x[1])*cos(1-exp(x[1])*x[2])]];

julia> J = zero(J_ref);

julia> df = zero(x);

julia> df_ref = diag(J_ref);

julia> epsilon = zero(real.(x));

julia> forward_cache = DiffEqDiffTools.JacobianCache(x,Val{:forward});

julia> central_cache = DiffEqDiffTools.JacobianCache(x);

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  752 bytes
  allocs estimate:  6
  --------------
  minimum time:     520.550 ns (0.00% GC)
  median time:      552.343 ns (0.00% GC)
  mean time:        644.422 ns (12.37% GC)
  maximum time:     228.696 μs (99.62% GC)
  --------------
  samples:          10000
  evals/sample:     191

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  304 bytes
  allocs estimate:  2
  --------------
  minimum time:     414.520 ns (0.00% GC)
  median time:      418.845 ns (0.00% GC)
  mean time:        472.140 ns (8.43% GC)
  maximum time:     217.488 μs (99.74% GC)
  --------------
  samples:          10000
  evals/sample:     200

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x)
BenchmarkTools.Trial: 
  memory estimate:  672 bytes
  allocs estimate:  6
  --------------
  minimum time:     644.091 ns (0.00% GC)
  median time:      677.335 ns (0.00% GC)
  mean time:        775.216 ns (11.02% GC)
  maximum time:     275.317 μs (99.61% GC)
  --------------
  samples:          10000
  evals/sample:     164

This PR:

julia> using DiffEqDiffTools

julia> using Compat, Compat.LinearAlgebra

julia> using BenchmarkTools
[ Info: Recompiling stale cache file /home/celrod/.julia/compiled/v0.7/BenchmarkTools/ZXPQ.ji for module BenchmarkTools
WARNING: importing deprecated binding Base.uninitialized into Common.
WARNING: Base.uninitialized is deprecated, use undef instead.
  likely near /home/celrod/.julia/packages/JSON/dIBT/src/bytes.jl:47
┌ Warning: `hex(n, pad)` is deprecated, use `string(n, base=16, pad=pad)` instead.
│   caller = top-level scope at <missing>:56
└ @ Core <missing>:56
WARNING: Broadcast.Scalar is deprecated, use DefaultArrayStyle{0} instead.
  likely near /home/celrod/.julia/packages/Nullables/IK5w/src/nullable.jl:412
WARNING: importing deprecated binding Base.uninitialized into BenchmarkTools.
WARNING: Base.uninitialized is deprecated, use undef instead.
  likely near /home/celrod/.julia/packages/BenchmarkTools/SFk3/src/execution.jl:135
┌ Warning: `using A.B` will only be allowed for modules, not single bindings. Use `using A: B` instead
│   caller = ip:0x0
└ @ Core :-1

julia> # TODO: add tests for GPUArrays
       # TODO: add tests for DEDataArrays

       # Derivative tests
       x = collect(Compat.range(-2π, stop=2π, length=100));

julia> y = sin.(x);

julia> df = fill(0.0,100);

julia> epsilon = fill(0.0,100);

julia> df_ref = cos.(x);

julia> forward_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:forward});

julia> central_cache = DiffEqDiffTools.DerivativeCache(x, nothing, epsilon, Val{:central});

julia> complex_cache = DiffEqDiffTools.DerivativeCache(x, nothing, nothing, Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, π/4, Val{:forward})
┌ Warning: `indmin` is deprecated, use `argmin` instead.
│   caller = minimum at trials.jl:112 [inlined]
└ @ Core trials.jl:112
┌ Warning: `indmax` is deprecated, use `argmax` instead.
│   caller = maximum at trials.jl:117 [inlined]
└ @ Core trials.jl:117
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     17.721 ns (0.00% GC)
  median time:      18.067 ns (0.00% GC)
  mean time:        18.306 ns (0.00% GC)
  maximum time:     46.807 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, π/4, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     21.495 ns (0.00% GC)
  median time:      21.560 ns (0.00% GC)
  mean time:        22.150 ns (0.00% GC)
  maximum time:     65.560 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, π/4, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     23.035 ns (0.00% GC)
  median time:      23.091 ns (0.00% GC)
  mean time:        23.506 ns (0.00% GC)
  maximum time:     61.996 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     996

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     1.908 μs (0.00% GC)
  median time:      1.941 μs (0.00% GC)
  mean time:        2.551 μs (19.49% GC)
  maximum time:     3.663 ms (99.88% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     1.994 μs (0.00% GC)
  median time:      2.026 μs (0.00% GC)
  mean time:        2.636 μs (18.81% GC)
  maximum time:     3.648 ms (99.87% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.698 μs (0.00% GC)
  median time:      2.737 μs (0.00% GC)
  mean time:        3.406 μs (16.07% GC)
  maximum time:     4.018 ms (99.87% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  928 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.045 μs (0.00% GC)
  median time:      1.191 μs (0.00% GC)
  mean time:        1.682 μs (28.96% GC)
  maximum time:     3.656 ms (99.93% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:central}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.006 μs (0.00% GC)
  median time:      2.047 μs (0.00% GC)
  mean time:        2.778 μs (20.14% GC)
  maximum time:     4.119 ms (99.88% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(sin, $x, Val{:complex}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  1.77 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.633 μs (0.00% GC)
  median time:      2.667 μs (0.00% GC)
  mean time:        3.365 μs (16.55% GC)
  maximum time:     4.096 ms (99.88% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.683 μs (0.00% GC)
  median time:      1.830 μs (0.00% GC)
  mean time:        2.309 μs (21.17% GC)
  maximum time:     3.691 ms (99.88% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.756 μs (0.00% GC)
  median time:      1.902 μs (0.00% GC)
  mean time:        2.396 μs (20.36% GC)
  maximum time:     3.671 ms (99.89% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     2.363 μs (0.00% GC)
  median time:      2.534 μs (0.00% GC)
  mean time:        3.070 μs (17.67% GC)
  maximum time:     4.128 ms (99.89% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  32 bytes
  allocs estimate:  1
  --------------
  minimum time:     999.000 ns (0.00% GC)
  median time:      1.006 μs (0.00% GC)
  mean time:        1.025 μs (0.00% GC)
  maximum time:     3.730 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:central}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.758 μs (0.00% GC)
  median time:      1.867 μs (0.00% GC)
  mean time:        2.398 μs (20.61% GC)
  maximum time:     3.721 ms (99.90% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, Val{:complex}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     2.363 μs (0.00% GC)
  median time:      2.533 μs (0.00% GC)
  mean time:        3.052 μs (17.69% GC)
  maximum time:     4.106 ms (99.89% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     1.005 μs (0.00% GC)
  median time:      1.011 μs (0.00% GC)
  mean time:        1.028 μs (0.00% GC)
  maximum time:     3.702 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     1.714 μs (0.00% GC)
  median time:      1.720 μs (0.00% GC)
  mean time:        1.788 μs (0.00% GC)
  maximum time:     6.398 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, sin, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     2.323 μs (0.00% GC)
  median time:      2.338 μs (0.00% GC)
  mean time:        2.558 μs (0.00% GC)
  maximum time:     9.086 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> x = x + im*x;

julia> f(x) = sin(x) + cos(x);

julia> y = f.(x);

julia> df = zero(x);

julia> epsilon = similar(real(x));

julia> df_ref = cos.(x) - sin.(x);

julia> forward_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:forward});

julia> central_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:central});
┌ Warning: Pre-computed function values are only useful for fdtype==Val(:forward).
└ @ DiffEqDiffTools derivatives.jl:39

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4+im*π/4, Val{:forward}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     192.256 ns (0.00% GC)
  median time:      192.604 ns (0.00% GC)
  mean time:        202.819 ns (0.00% GC)
  maximum time:     439.796 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     636

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4+im*π/4, Val{:central}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     185.499 ns (0.00% GC)
  median time:      191.591 ns (0.00% GC)
  mean time:        194.382 ns (0.00% GC)
  maximum time:     423.830 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     671

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     19.791 μs (0.00% GC)
  median time:      20.806 μs (0.00% GC)
  mean time:        23.098 μs (4.90% GC)
  maximum time:     11.380 ms (99.55% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     19.658 μs (0.00% GC)
  median time:      20.662 μs (0.00% GC)
  mean time:        21.958 μs (5.15% GC)
  maximum time:     11.358 ms (99.56% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, eltype($x), $y)
BenchmarkTools.Trial: 
  memory estimate:  2.67 KiB
  allocs estimate:  3
  --------------
  minimum time:     10.848 μs (0.00% GC)
  median time:      11.732 μs (0.00% GC)
  mean time:        11.972 μs (0.00% GC)
  maximum time:     42.965 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:central}, eltype($x))#, $y)
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     20.768 μs (0.00% GC)
  median time:      21.677 μs (0.00% GC)
  mean time:        23.634 μs (5.05% GC)
  maximum time:     11.979 ms (99.58% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  1.80 KiB
  allocs estimate:  2
  --------------
  minimum time:     10.304 μs (0.00% GC)
  median time:      10.611 μs (0.00% GC)
  mean time:        11.114 μs (0.00% GC)
  maximum time:     40.608 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     19.422 μs (0.00% GC)
  median time:      19.491 μs (0.00% GC)
  mean time:        19.841 μs (0.00% GC)
  maximum time:     50.232 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:central}, eltype($x))
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     19.226 μs (0.00% GC)
  median time:      19.340 μs (0.00% GC)
  mean time:        20.088 μs (0.00% GC)
  maximum time:     63.353 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, eltype($x), $y)
BenchmarkTools.Trial: 
  memory estimate:  928 bytes
  allocs estimate:  2
  --------------
  minimum time:     10.246 μs (0.00% GC)
  median time:      10.305 μs (0.00% GC)
  mean time:        10.816 μs (0.00% GC)
  maximum time:     35.288 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, eltype($x), $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  32 bytes
  allocs estimate:  1
  --------------
  minimum time:     10.228 μs (0.00% GC)
  median time:      10.286 μs (0.00% GC)
  mean time:        10.850 μs (0.00% GC)
  maximum time:     37.627 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     10.199 μs (0.00% GC)
  median time:      10.231 μs (0.00% GC)
  mean time:        10.759 μs (0.00% GC)
  maximum time:     37.554 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     19.172 μs (0.00% GC)
  median time:      19.260 μs (0.00% GC)
  mean time:        19.949 μs (0.00% GC)
  maximum time:     61.465 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> x = collect(Compat.range(-2π, stop=2π, length=100));

julia> f(x) = sin(x) + im*cos(x);

julia> y = f.(x);

julia> df = fill(zero(Complex{eltype(x)}), size(x));

julia> epsilon = similar(real(x));

julia> df_ref = cos.(x) - im*sin.(x);

julia> forward_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:forward}, eltype(df));

julia> central_cache = DiffEqDiffTools.DerivativeCache(x, y, epsilon, Val{:central}, eltype(df));
┌ Warning: Pre-computed function values are only useful for fdtype==Val(:forward).
└ @ DiffEqDiffTools derivatives.jl:39

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4, Val{:forward}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     39.467 ns (0.00% GC)
  median time:      39.563 ns (0.00% GC)
  mean time:        40.939 ns (0.00% GC)
  maximum time:     111.548 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     991

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, π/4, Val{:central}, Val{:Complex})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     36.597 ns (0.00% GC)
  median time:      36.648 ns (0.00% GC)
  mean time:        37.904 ns (0.00% GC)
  maximum time:     115.986 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     992

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     3.717 μs (0.00% GC)
  median time:      3.820 μs (0.00% GC)
  mean time:        5.148 μs (14.47% GC)
  maximum time:     5.386 ms (99.85% GC)
  --------------
  samples:          10000
  evals/sample:     8

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:central}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  2.66 KiB
  allocs estimate:  3
  --------------
  minimum time:     3.592 μs (0.00% GC)
  median time:      3.710 μs (0.00% GC)
  mean time:        4.526 μs (14.94% GC)
  maximum time:     4.837 ms (99.86% GC)
  --------------
  samples:          10000
  evals/sample:     8

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, Complex{eltype($x)}, $y)
BenchmarkTools.Trial: 
  memory estimate:  2.67 KiB
  allocs estimate:  3
  --------------
  minimum time:     2.231 μs (0.00% GC)
  median time:      2.291 μs (0.00% GC)
  mean time:        3.054 μs (20.42% GC)
  maximum time:     4.422 ms (99.88% GC)
  --------------
  samples:          10000
  evals/sample:     9

julia> @benchmark DiffEqDiffTools.finite_difference_derivative(f, $x, Val{:forward}, Complex{eltype($x)}, $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  1.80 KiB
  allocs estimate:  2
  --------------
  minimum time:     1.890 μs (0.00% GC)
  median time:      1.937 μs (0.00% GC)
  mean time:        2.640 μs (21.04% GC)
  maximum time:     3.961 ms (99.88% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     3.366 μs (0.00% GC)
  median time:      3.548 μs (0.00% GC)
  mean time:        4.186 μs (15.21% GC)
  maximum time:     4.861 ms (99.87% GC)
  --------------
  samples:          10000
  evals/sample:     8

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:central}, Complex{eltype($x)})
BenchmarkTools.Trial: 
  memory estimate:  912 bytes
  allocs estimate:  2
  --------------
  minimum time:     3.212 μs (0.00% GC)
  median time:      3.388 μs (0.00% GC)
  mean time:        4.046 μs (16.09% GC)
  maximum time:     5.004 ms (99.88% GC)
  --------------
  samples:          10000
  evals/sample:     8

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, Complex{eltype($x)}, $y)
BenchmarkTools.Trial: 
  memory estimate:  928 bytes
  allocs estimate:  2
  --------------
  minimum time:     1.815 μs (0.00% GC)
  median time:      1.876 μs (0.00% GC)
  mean time:        2.458 μs (21.07% GC)
  maximum time:     3.910 ms (99.90% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, Val{:forward}, Complex{eltype($x)}, $y, $epsilon)
BenchmarkTools.Trial: 
  memory estimate:  32 bytes
  allocs estimate:  1
  --------------
  minimum time:     1.772 μs (0.00% GC)
  median time:      1.778 μs (0.00% GC)
  mean time:        1.818 μs (0.00% GC)
  maximum time:     5.546 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     1.773 μs (0.00% GC)
  median time:      1.780 μs (0.00% GC)
  mean time:        1.804 μs (0.00% GC)
  maximum time:     6.390 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_derivative!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     3.164 μs (0.00% GC)
  median time:      3.169 μs (0.00% GC)
  mean time:        3.203 μs (0.00% GC)
  maximum time:     8.214 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     8

julia> # Gradient tests
       f(x) = 2x[1] + x[2]^2;

julia> x = rand(2);

julia> fx = f(x);

julia> df = fill(0.0, 2);

julia> df_ref = [2., 2*x[2]];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> complex_cache = DiffEqDiffTools.GradientCache(df,x,Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  96 bytes
  allocs estimate:  1
  --------------
  minimum time:     29.811 ns (0.00% GC)
  median time:      31.878 ns (0.00% GC)
  mean time:        42.499 ns (21.65% GC)
  maximum time:     39.826 μs (99.86% GC)
  --------------
  samples:          10000
  evals/sample:     995

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  96 bytes
  allocs estimate:  1
  --------------
  minimum time:     41.669 ns (0.00% GC)
  median time:      43.159 ns (0.00% GC)
  mean time:        54.724 ns (17.10% GC)
  maximum time:     39.894 μs (99.80% GC)
  --------------
  samples:          10000
  evals/sample:     991

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  224 bytes
  allocs estimate:  3
  --------------
  minimum time:     76.059 ns (0.00% GC)
  median time:      81.975 ns (0.00% GC)
  mean time:        106.290 ns (20.09% GC)
  maximum time:     41.317 μs (99.67% GC)
  --------------
  samples:          10000
  evals/sample:     976

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     11.125 ns (0.00% GC)
  median time:      11.186 ns (0.00% GC)
  mean time:        11.291 ns (0.00% GC)
  maximum time:     33.937 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     999

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     18.875 ns (0.00% GC)
  median time:      18.932 ns (0.00% GC)
  mean time:        19.136 ns (0.00% GC)
  maximum time:     49.395 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  128 bytes
  allocs estimate:  2
  --------------
  minimum time:     51.735 ns (0.00% GC)
  median time:      55.657 ns (0.00% GC)
  mean time:        69.517 ns (17.25% GC)
  maximum time:     41.734 μs (99.77% GC)
  --------------
  samples:          10000
  evals/sample:     986

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     11.098 ns (0.00% GC)
  median time:      11.164 ns (0.00% GC)
  mean time:        11.370 ns (0.00% GC)
  maximum time:     46.470 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     999

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     18.945 ns (0.00% GC)
  median time:      19.029 ns (0.00% GC)
  mean time:        19.596 ns (0.00% GC)
  maximum time:     63.767 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     24.605 ns (0.00% GC)
  median time:      24.646 ns (0.00% GC)
  mean time:        24.975 ns (0.00% GC)
  maximum time:     67.475 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     996

julia> f(x) = 2x[1] + im*2x[1] + x[2]^2;

julia> x = x + im*x;

julia> fx = f(x);

julia> df = zero(x);

julia> df_ref = conj([2.0+2.0*im, 2.0*x[2]]);

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     131.069 ns (0.00% GC)
  median time:      132.633 ns (0.00% GC)
  mean time:        149.753 ns (7.45% GC)
  maximum time:     47.450 μs (99.62% GC)
  --------------
  samples:          10000
  evals/sample:     881

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     131.899 ns (0.00% GC)
  median time:      133.099 ns (0.00% GC)
  mean time:        148.605 ns (7.40% GC)
  maximum time:     45.832 μs (99.60% GC)
  --------------
  samples:          10000
  evals/sample:     881

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     111.359 ns (0.00% GC)
  median time:      111.486 ns (0.00% GC)
  mean time:        112.785 ns (0.00% GC)
  maximum time:     252.453 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     925

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     108.859 ns (0.00% GC)
  median time:      108.974 ns (0.00% GC)
  mean time:        110.823 ns (0.00% GC)
  maximum time:     218.515 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     930

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     111.339 ns (0.00% GC)
  median time:      114.335 ns (0.00% GC)
  mean time:        116.531 ns (0.00% GC)
  maximum time:     244.549 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     925

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     108.927 ns (0.00% GC)
  median time:      109.025 ns (0.00% GC)
  mean time:        112.104 ns (0.00% GC)
  maximum time:     256.600 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     930

julia> f(x) = sum(abs2, x);

julia> x = ones(2) * (1 + im);

julia> fx = f(x);

julia> df = zero(x);

julia> df_ref = 2*x;

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     115.237 ns (0.00% GC)
  median time:      116.954 ns (0.00% GC)
  mean time:        134.508 ns (8.45% GC)
  maximum time:     49.097 μs (99.69% GC)
  --------------
  samples:          10000
  evals/sample:     918

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     95.224 ns (0.00% GC)
  median time:      96.738 ns (0.00% GC)
  mean time:        112.304 ns (9.93% GC)
  maximum time:     45.138 μs (99.67% GC)
  --------------
  samples:          10000
  evals/sample:     950

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     89.563 ns (0.00% GC)
  median time:      90.009 ns (0.00% GC)
  mean time:        93.205 ns (0.00% GC)
  maximum time:     185.762 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     957

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     74.311 ns (0.00% GC)
  median time:      74.486 ns (0.00% GC)
  mean time:        75.431 ns (0.00% GC)
  maximum time:     155.830 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     972

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     89.325 ns (0.00% GC)
  median time:      90.015 ns (0.00% GC)
  mean time:        91.842 ns (0.00% GC)
  maximum time:     199.147 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     957

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     74.502 ns (0.00% GC)
  median time:      74.724 ns (0.00% GC)
  mean time:        76.747 ns (0.00% GC)
  maximum time:     178.991 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     972

julia> f(x) = 2*x[1] + im*x[2]^2;

julia> x = ones(2);

julia> fx = f(x);

julia> df = fill(zero(eltype(fx)), size(x));

julia> df_ref = [2.0, -im*2*x[2]];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward},eltype(df));

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central},eltype(df));

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  240 bytes
  allocs estimate:  3
  --------------
  minimum time:     115.190 ns (0.00% GC)
  median time:      122.872 ns (0.00% GC)
  mean time:        148.452 ns (13.81% GC)
  maximum time:     49.488 μs (99.61% GC)
  --------------
  samples:          10000
  evals/sample:     921

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  240 bytes
  allocs estimate:  3
  --------------
  minimum time:     132.009 ns (0.00% GC)
  median time:      139.552 ns (0.00% GC)
  mean time:        164.695 ns (12.56% GC)
  maximum time:     47.681 μs (99.62% GC)
  --------------
  samples:          10000
  evals/sample:     888

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  128 bytes
  allocs estimate:  2
  --------------
  minimum time:     91.867 ns (0.00% GC)
  median time:      96.224 ns (0.00% GC)
  mean time:        112.450 ns (11.78% GC)
  maximum time:     43.421 μs (99.69% GC)
  --------------
  samples:          10000
  evals/sample:     955

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central}, eltype($df))
BenchmarkTools.Trial: 
  memory estimate:  128 bytes
  allocs estimate:  2
  --------------
  minimum time:     107.674 ns (0.00% GC)
  median time:      113.280 ns (0.00% GC)
  mean time:        130.210 ns (10.45% GC)
  maximum time:     44.316 μs (99.64% GC)
  --------------
  samples:          10000
  evals/sample:     932

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     69.847 ns (0.00% GC)
  median time:      70.088 ns (0.00% GC)
  mean time:        71.917 ns (0.00% GC)
  maximum time:     166.475 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     976

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     84.920 ns (0.00% GC)
  median time:      85.005 ns (0.00% GC)
  mean time:        85.831 ns (0.00% GC)
  maximum time:     167.302 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     962

julia> f(df,x) = (df[1]=sin(x); df[2]=cos(x); df);

julia> x = 2π * rand();

julia> fx = fill(0.0,2);

julia> f(fx,x);

julia> df = fill(0.0,2);

julia> df_ref = [cos(x), -sin(x)];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> complex_cache = DiffEqDiffTools.GradientCache(df,x,Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward}, eltype($x), Val{true}, $fx)
BenchmarkTools.Trial: 
  memory estimate:  368 bytes
  allocs estimate:  7
  --------------
  minimum time:     373.532 ns (0.00% GC)
  median time:      382.015 ns (0.00% GC)
  mean time:        449.733 ns (11.33% GC)
  maximum time:     203.949 μs (99.72% GC)
  --------------
  samples:          10000
  evals/sample:     205

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central}, eltype($x), Val{true}, $fx)
BenchmarkTools.Trial: 
  memory estimate:  368 bytes
  allocs estimate:  7
  --------------
  minimum time:     456.335 ns (0.00% GC)
  median time:      467.117 ns (0.00% GC)
  mean time:        535.491 ns (9.69% GC)
  maximum time:     220.540 μs (99.61% GC)
  --------------
  samples:          10000
  evals/sample:     197

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:complex}, eltype($x), Val{true}, $fx)
BenchmarkTools.Trial: 
  memory estimate:  272 bytes
  allocs estimate:  6
  --------------
  minimum time:     507.622 ns (0.00% GC)
  median time:      513.891 ns (0.00% GC)
  mean time:        579.743 ns (8.23% GC)
  maximum time:     220.583 μs (99.66% GC)
  --------------
  samples:          10000
  evals/sample:     193

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  224 bytes
  allocs estimate:  3
  --------------
  minimum time:     83.366 ns (0.00% GC)
  median time:      87.540 ns (0.00% GC)
  mean time:        112.120 ns (19.08% GC)
  maximum time:     43.612 μs (99.67% GC)
  --------------
  samples:          10000
  evals/sample:     966

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  224 bytes
  allocs estimate:  3
  --------------
  minimum time:     94.439 ns (0.00% GC)
  median time:      98.266 ns (0.00% GC)
  mean time:        123.535 ns (17.82% GC)
  maximum time:     45.670 μs (99.69% GC)
  --------------
  samples:          10000
  evals/sample:     955

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:complex})
BenchmarkTools.Trial: 
  memory estimate:  128 bytes
  allocs estimate:  2
  --------------
  minimum time:     81.254 ns (0.00% GC)
  median time:      84.564 ns (0.00% GC)
  mean time:        100.356 ns (13.46% GC)
  maximum time:     43.173 μs (99.69% GC)
  --------------
  samples:          10000
  evals/sample:     967

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     38.628 ns (0.00% GC)
  median time:      38.652 ns (0.00% GC)
  mean time:        39.583 ns (0.00% GC)
  maximum time:     113.124 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     992

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     46.638 ns (0.00% GC)
  median time:      46.775 ns (0.00% GC)
  mean time:        47.752 ns (0.00% GC)
  maximum time:     126.520 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     989

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     52.790 ns (0.00% GC)
  median time:      52.820 ns (0.00% GC)
  mean time:        53.550 ns (0.00% GC)
  maximum time:     134.256 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     986

julia> f(df,x) = (df[1]=sin(x); df[2]=cos(x); df);

julia> x = (2π * rand()) * (1 + im);

julia> fx = fill(zero(typeof(x)), 2);

julia> f(fx,x);

julia> df = zero(fx);

julia> df_ref = [cos(x), -sin(x)];

julia> forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward});

julia> central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central});

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:forward}, eltype($x), Val{true}, fx)
BenchmarkTools.Trial: 
  memory estimate:  464 bytes
  allocs estimate:  7
  --------------
  minimum time:     889.911 ns (0.00% GC)
  median time:      906.222 ns (0.00% GC)
  mean time:        1.082 μs (13.38% GC)
  maximum time:     947.000 μs (99.81% GC)
  --------------
  samples:          10000
  evals/sample:     45

julia> @benchmark DiffEqDiffTools.finite_difference_gradient(f, $x, Val{:central}, eltype($x), Val{true}, fx)
BenchmarkTools.Trial: 
  memory estimate:  464 bytes
  allocs estimate:  7
  --------------
  minimum time:     1.054 μs (0.00% GC)
  median time:      1.156 μs (0.00% GC)
  mean time:        1.676 μs (31.83% GC)
  maximum time:     4.237 ms (99.90% GC)
  --------------
  samples:          10000
  evals/sample:     10

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:forward})
BenchmarkTools.Trial: 
  memory estimate:  256 bytes
  allocs estimate:  3
  --------------
  minimum time:     258.281 ns (0.00% GC)
  median time:      260.913 ns (0.00% GC)
  mean time:        300.476 ns (11.00% GC)
  maximum time:     120.120 μs (99.68% GC)
  --------------
  samples:          10000
  evals/sample:     352

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, Val{:central})
BenchmarkTools.Trial: 
  memory estimate:  256 bytes
  allocs estimate:  3
  --------------
  minimum time:     266.629 ns (0.00% GC)
  median time:      270.184 ns (0.00% GC)
  mean time:        310.804 ns (11.09% GC)
  maximum time:     131.338 μs (99.71% GC)
  --------------
  samples:          10000
  evals/sample:     326

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     212.111 ns (0.00% GC)
  median time:      212.594 ns (0.00% GC)
  mean time:        215.109 ns (0.00% GC)
  maximum time:     486.970 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     540

julia> @benchmark DiffEqDiffTools.finite_difference_gradient!($df, f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     218.840 ns (0.00% GC)
  median time:      221.911 ns (0.00% GC)
  mean time:        224.481 ns (0.00% GC)
  maximum time:     542.294 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     506

julia> # Jacobian tests
       function f(fvec,x)
           fvec[1] = (x[1]+3)*(x[2]^3-7)+18
           fvec[2] = sin(x[2]*exp(x[1])-1)
       end
f (generic function with 2 methods)

julia> x = rand(2); y = rand(2);

julia> f(y,x);

julia> J_ref = [[-7+x[2]^3 3*(3+x[1])*x[2]^2]; [exp(x[1])*x[2]*cos(1-exp(x[1])*x[2]) exp(x[1])*cos(1-exp(x[1])*x[2])]];

julia> J = zero(J_ref);

julia> df = zero(x);

julia> df_ref = diag(J_ref);

julia> epsilon = zero(x);

julia> forward_cache = DiffEqDiffTools.JacobianCache(x,Val{:forward});

julia> central_cache = DiffEqDiffTools.JacobianCache(x);

julia> complex_cache = DiffEqDiffTools.JacobianCache(x,Val{:complex});

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  496 bytes
  allocs estimate:  5
  --------------
  minimum time:     283.326 ns (0.00% GC)
  median time:      303.916 ns (0.00% GC)
  mean time:        371.415 ns (16.19% GC)
  maximum time:     152.237 μs (99.70% GC)
  --------------
  samples:          10000
  evals/sample:     285

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     147.431 ns (0.00% GC)
  median time:      149.231 ns (0.00% GC)
  mean time:        166.191 ns (7.99% GC)
  maximum time:     52.027 μs (99.59% GC)
  --------------
  samples:          10000
  evals/sample:     832

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x)
BenchmarkTools.Trial: 
  memory estimate:  432 bytes
  allocs estimate:  5
  --------------
  minimum time:     341.834 ns (0.00% GC)
  median time:      359.721 ns (0.00% GC)
  mean time:        433.467 ns (15.07% GC)
  maximum time:     203.843 μs (99.67% GC)
  --------------
  samples:          10000
  evals/sample:     217

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $complex_cache)
BenchmarkTools.Trial: 
  memory estimate:  112 bytes
  allocs estimate:  1
  --------------
  minimum time:     154.511 ns (0.00% GC)
  median time:      155.438 ns (0.00% GC)
  mean time:        172.372 ns (7.93% GC)
  maximum time:     53.055 μs (99.58% GC)
  --------------
  samples:          10000
  evals/sample:     807

julia> function f(fvec,x)
           fvec[1] = (im*x[1]+3)*(x[2]^3-7)+18
           fvec[2] = sin(x[2]*exp(x[1])-1)
       end
f (generic function with 2 methods)

julia> x = rand(2) + im*rand(2);

julia> y = similar(x);

julia> f(y,x);

julia> J_ref = [[im*(-7+x[2]^3) 3*(3+im*x[1])*x[2]^2]; [exp(x[1])*x[2]*cos(1-exp(x[1])*x[2]) exp(x[1])*cos(1-exp(x[1])*x[2])]];

julia> J = zero(J_ref);

julia> df = zero(x);

julia> df_ref = diag(J_ref);

julia> epsilon = zero(real.(x));

julia> forward_cache = DiffEqDiffTools.JacobianCache(x,Val{:forward});

julia> central_cache = DiffEqDiffTools.JacobianCache(x);

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $forward_cache)
BenchmarkTools.Trial: 
  memory estimate:  592 bytes
  allocs estimate:  5
  --------------
  minimum time:     472.092 ns (0.00% GC)
  median time:      492.474 ns (0.00% GC)
  mean time:        572.230 ns (11.69% GC)
  maximum time:     220.907 μs (99.58% GC)
  --------------
  samples:          10000
  evals/sample:     196

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x, $central_cache)
BenchmarkTools.Trial: 
  memory estimate:  144 bytes
  allocs estimate:  1
  --------------
  minimum time:     361.038 ns (0.00% GC)
  median time:      363.359 ns (0.00% GC)
  mean time:        405.296 ns (7.80% GC)
  maximum time:     205.764 μs (99.75% GC)
  --------------
  samples:          10000
  evals/sample:     209

julia> @benchmark DiffEqDiffTools.finite_difference_jacobian(f, $x)
BenchmarkTools.Trial: 
  memory estimate:  512 bytes
  allocs estimate:  5
  --------------
  minimum time:     579.111 ns (0.00% GC)
  median time:      600.061 ns (0.00% GC)
  mean time:        682.239 ns (9.98% GC)
  maximum time:     241.986 μs (99.64% GC)
  --------------
  samples:          10000
  evals/sample:     180

@ChrisRackauckas
Copy link
Member

Looks like there's some issues then on v0.6 but on v0.7 it's beneficial. To not over-complicate things, I think it's fine to just bump the minimum version to julia 0.7- and call it a day. I don't want to waste dev time on version compatibility here. But having upstream PRs for the API change will be required though, so that way it eases the v0.7 transition for those libraries.

BTW, the Vals that are kept in v0.7, are they still needed even with the constant propagation added to the compiler? I though we could get rid of at least almost all of the Vals.

@dextorious
Copy link
Contributor

Just a note before future benchmarks - for benchmarking calls that only take tens of microseconds or less you want to bump up BenchmarkTools.DEFAULT_PARAMETERS.samples way up, I usually set it to a billion just to make sure it always gets statistically reasonable results. It still doesn't eliminate all issues with timing noise, but it can make a significant difference in some cases.

On the larger point, I'm not up to date with all the v0.7 changes, but getting rid of the Vals would be a big win if we can get it for free.

@chriselrod
Copy link
Author

chriselrod commented Mar 30, 2018

@inferred(err_func(DiffEqDiffTools.finite_difference_gradient(f, x, Val{:central}(), eltype(x), Val{true}(), fx), df_ref)) < 1.0e-7
   Evaluated: 1.1482509078254509e-7 < 1.0e-7

Any thoughts on this?

Leading up to it, we have

x = (2π * rand()) * (1 + im)
fx = fill(zero(typeof(x)), 2)
f(fx,x)
df = zero(fx)
df_ref = [cos(x), -sin(x)]
forward_cache = DiffEqDiffTools.GradientCache(df,x,Val{:forward}())
central_cache = DiffEqDiffTools.GradientCache(df,x,Val{:central}())

so it seems to happen sporadically for random values of x.
Never saw it happen when testing locally, but this is the second time on Travis.
I don't want to loosen test tolerances or anything like that without hearing from you guys.

Also, anything I can do about Appveyor?
This is pure Julia, so as long as Julia 0.7-dev is installed on Windows, I have a hard time imagining there being any issues.

I'll look into what, if any, constant propagation we can do to avoid Vals. Seems like if the constant is known at compile time (eg, someone implements a function that chooses :complex or :central by default), it should be possible. But some early tests didn't look good.
Isn't the propagation even supposed to happen through non-inlined function calls?

On number of samples, I remember @dextorious recommending BenchmarkTools.DEFAULT_PARAMETERS.samples about half a year ago. I'll keep that in mind later. How accurate is BenchmarkTools at the nanosecond level? Does it time by taking a per-sample average?

EDIT:
This should have been caught in the tests
633a91c
so I'll add something.

@ExpandingMan
Copy link

Any chance of resurrecting this PR? 0.7 is nigh and a number of packages depend on this.

@ChrisRackauckas
Copy link
Member

This includes an API change, so at least the DiffEq packages need an upstream PR to match it. The JuliaNLSolvers stack should get PRs as well.

@ChrisRackauckas
Copy link
Member

Closing this because it has API changes mixed with the compatibility change. It's not that I don't want the API changes, it's that requiring that every downstream library does a breaking API change at the same time as its v0.7 upgrade complicates things so much, especially when it's clear that no one wants to make downstream PRs to actually make this an attainable reality. We will v0.7 upgrade (with the help of Femtocleaner) and then we'll be open to a PR like this API change.

@chriselrod
Copy link
Author

chriselrod commented Jun 29, 2018 via email

@ChrisRackauckas
Copy link
Member

Oh cool. Then it should be easy to revive this. I just wanted to do the easiest upgrade possible though since the v0.7 compatibility upgrades are going to be a large amount of work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants