Skip to content

Commit 49292ee

Browse files
simonbyrnejrevels
authored andcommitted
tweak tag generation (#279)
* tweak tag generation This does 3 related things 1. Re-enables void tags by providing `nothing` as function argument 2. Allows opt out of tag checking via `Val{false}()` argument. 3. Simplifies the Hessian to use the same tag (which is useful for my own purposes of computing Riemannian metrics, but also makes the code a bit cleaner). * add docs * derivative fixes * add tests for tag checks * add tag docs
1 parent 43dbff0 commit 49292ee

File tree

9 files changed

+138
-57
lines changed

9 files changed

+138
-57
lines changed

docs/src/user/advanced.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,36 @@ Note that whether or not SIMD instructions can actually be used will depend on y
283283
and Julia build. For example, pre-built Julia binaries might not emit vectorized LLVM
284284
bitcode. To overcome this specific issue, you can [locally rebuild Julia's system
285285
image](http://docs.julialang.org/en/latest/devdocs/sysimg).
286+
287+
## Custom tags and tag checking
288+
289+
The `Dual` type includes a "tag" parameter indicating the particular function call to
290+
which it belongs. This is to avoid a problem known as [_perturbation
291+
confusion_](https://github.com/JuliaDiff/ForwardDiff.jl/issues/83) which can arise when
292+
there are nested differentiation calls. Tags are automatically generated as part of the
293+
appropriate config object, and the tag is checked when the config is used as part of a
294+
differentiation call (`derivative`, `gradient`, etc.): an `InvalidTagException` will be
295+
thrown if the incorrect config object is used.
296+
297+
This checking can sometimes be inconvenient, and there are certain cases where you may
298+
want to disable this checking.
299+
300+
!!! warning
301+
Disabling tag checking should only be done with caution, especially if the code itself
302+
could be used inside another differentiation call.
303+
304+
1. (preferred) Provide an extra `Val{false}()` argument to the differentiation function, e.g.
305+
```julia
306+
cfg = ForwarDiff.GradientConfig(g, x)
307+
ForwarDiff.gradient(f, x, cfg, Val{false}())
308+
```
309+
If using as part of a library, the tag can be checked manually via
310+
```julia
311+
ForwardDiff.checktag(cfg, g, x)
312+
```
313+
314+
2. (discouraged) Construct the config object with `nothing` instead of a function argument, e.g.
315+
```julia
316+
cfg = GradientConfig(nothing, x)
317+
gradient(f, x, cfg)
318+
```

src/config.jl

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,21 @@ function Tag(f::F, ::Type{V}) where {F,V}
1818
Tag{F,V}()
1919
end
2020

21+
Tag(::Void, ::Type{V}) where {V} = nothing
22+
23+
2124
@inline function (::Type{Tag{F1,V1}}, ::Type{Tag{F2,V2}}) where {F1,V1,F2,V2}
2225
tagcount(Tag{F1,V1}) < tagcount(Tag{F2,V2})
2326
end
2427

28+
struct InvalidTagException{E,O} <: Exception
29+
end
2530

31+
Base.showerror(io::IO, e::InvalidTagException{E,O}) where {E,O} =
32+
print(io, "Invalid Tag object:\n Expected $E,\n Observed $O.")
2633

2734
checktag(::Type{Tag{FT,VT}}, f::F, x::AbstractArray{V}) where {FT,VT,F,V} =
28-
error("Invalid Tag object:\n Expected $(Tag{F,V}),\n Observed $(Tag{FT,VT}).")
35+
throw(InvalidTagException{Tag{F,V},Tag{FT,VT}}())
2936

3037
checktag(::Type{Tag{F,V}}, f::F, x::AbstractArray{V}) where {F,V} = true
3138

@@ -80,6 +87,7 @@ function DerivativeConfig(f::F,
8087
return DerivativeConfig{T,typeof(duals)}(duals)
8188
end
8289

90+
checktag(::DerivativeConfig{T},f,x) where {T} = checktag(T,f,x)
8391
Base.eltype(::Type{DerivativeConfig{T,D}}) where {T,D} = eltype(D)
8492

8593
##################
@@ -115,6 +123,7 @@ function GradientConfig(f::F,
115123
return GradientConfig{T,V,N,typeof(duals)}(seeds, duals)
116124
end
117125

126+
checktag(::GradientConfig{T},f,x) where {T} = checktag(T,f,x)
118127
Base.eltype(::Type{GradientConfig{T,V,N,D}}) where {T,V,N,D} = Dual{T,V,N}
119128

120129
##################
@@ -179,15 +188,16 @@ function JacobianConfig(f::F,
179188
return JacobianConfig{T,X,N,typeof(duals)}(seeds, duals)
180189
end
181190

191+
checktag(::JacobianConfig{T},f,x) where {T} = checktag(T,f,x)
182192
Base.eltype(::Type{JacobianConfig{T,V,N,D}}) where {T,V,N,D} = Dual{T,V,N}
183193

184194
#################
185195
# HessianConfig #
186196
#################
187197

188-
struct HessianConfig{TG,TJ,V,N,DG,DJ} <: AbstractConfig{N}
189-
jacobian_config::JacobianConfig{TJ,V,N,DJ}
190-
gradient_config::GradientConfig{TG,Dual{TJ,V,N},N,DG}
198+
struct HessianConfig{T,V,N,DG,DJ} <: AbstractConfig{N}
199+
jacobian_config::JacobianConfig{T,V,N,DJ}
200+
gradient_config::GradientConfig{T,Dual{T,V,N},N,DG}
191201
end
192202

193203
"""
@@ -211,10 +221,9 @@ This constructor does not store/modify `x`.
211221
function HessianConfig(f::F,
212222
x::AbstractArray{V},
213223
chunk::Chunk = Chunk(x),
214-
tagj = Tag((f,gradient), V),
215-
tagg = Tag(f, Dual{typeof(tagj),V,chunksize(chunk)})) where {F,V}
216-
jacobian_config = JacobianConfig((f,gradient), x, chunk, tagj)
217-
gradient_config = GradientConfig(f, jacobian_config.duals, chunk, tagg)
224+
tag = Tag(f, V)) where {F,V}
225+
jacobian_config = JacobianConfig(f, x, chunk, tag)
226+
gradient_config = GradientConfig(f, jacobian_config.duals, chunk, tag)
218227
return HessianConfig(jacobian_config, gradient_config)
219228
end
220229

@@ -237,12 +246,12 @@ function HessianConfig(f::F,
237246
result::DiffResult,
238247
x::AbstractArray{V},
239248
chunk::Chunk = Chunk(x),
240-
tagj = Tag((f,typeof(gradient)), V),
241-
tagg = Tag(f, Dual{typeof(tagj),V,chunksize(chunk)})) where {F,V}
242-
jacobian_config = JacobianConfig((f,gradient), DiffResults.gradient(result), x, chunk, tagj)
243-
gradient_config = GradientConfig(f, jacobian_config.duals[2], chunk, tagg)
249+
tag = Tag(f, V)) where {F,V}
250+
jacobian_config = JacobianConfig((f,gradient), DiffResults.gradient(result), x, chunk, tag)
251+
gradient_config = GradientConfig(f, jacobian_config.duals[2], chunk, tag)
244252
return HessianConfig(jacobian_config, gradient_config)
245253
end
246254

247-
Base.eltype(::Type{HessianConfig{TG,TJ,V,N,DG,DJ}}) where {TG,TJ,V,N,DG,DJ} =
248-
Dual{TG,Dual{TJ,V,N},N}
255+
checktag(::HessianConfig{T},f,x) where {T} = checktag(T,f,x)
256+
Base.eltype(::Type{HessianConfig{T,V,N,DG,DJ}}) where {T,V,N,DG,DJ} =
257+
Dual{T,Dual{T,V,N},N}

src/derivative.jl

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@ This method assumes that `isa(f(x), Union{Real,AbstractArray})`.
1515
end
1616

1717
"""
18-
ForwardDiff.derivative(f!, y::AbstractArray, x::Real, cfg::DerivativeConfig = DerivativeConfig(f!, y, x))
18+
ForwardDiff.derivative(f!, y::AbstractArray, x::Real, cfg::DerivativeConfig = DerivativeConfig(f!, y, x), check=Val{true}())
1919
2020
Return `df!/dx` evaluated at `x`, assuming `f!` is called as `f!(y, x)` where the result is
2121
stored in `y`.
22+
23+
Set `check` to `Val{false}()` to disable tag checking. This can lead to perturbation confusion, so should be used with care.
2224
"""
2325
@inline function derivative(f!, y::AbstractArray, x::Real,
24-
cfg::DerivativeConfig{T} = DerivativeConfig(f!, y, x)) where {T}
25-
checktag(T, f!, x)
26+
cfg::DerivativeConfig{T} = DerivativeConfig(f!, y, x), ::Val{CHK}=Val{true}()) where {T, CHK}
27+
CHK && checktag(T, f!, x)
2628
ydual = cfg.duals
2729
seed!(ydual, y)
2830
f!(ydual, Dual{T}(x, one(x)))
@@ -48,15 +50,17 @@ This method assumes that `isa(f(x), Union{Real,AbstractArray})`.
4850
end
4951

5052
"""
51-
ForwardDiff.derivative!(result::Union{AbstractArray,DiffResult}, f!, y::AbstractArray, x::Real, cfg::DerivativeConfig = DerivativeConfig(f!, y, x))
53+
ForwardDiff.derivative!(result::Union{AbstractArray,DiffResult}, f!, y::AbstractArray, x::Real, cfg::DerivativeConfig = DerivativeConfig(f!, y, x), check=Val{true}())
5254
5355
Compute `df!/dx` evaluated at `x` and store the result(s) in `result`, assuming `f!` is
5456
called as `f!(y, x)` where the result is stored in `y`.
57+
58+
Set `check` to `Val{false}()` to disable tag checking. This can lead to perturbation confusion, so should be used with care.
5559
"""
5660
@inline function derivative!(result::Union{AbstractArray,DiffResult},
5761
f!, y::AbstractArray, x::Real,
58-
cfg::DerivativeConfig{T} = DerivativeConfig(f!, y, x)) where {T}
59-
checktag(T, f!, x)
62+
cfg::DerivativeConfig{T} = DerivativeConfig(f!, y, x), ::Val{CHK}=Val{true}()) where {T, CHK}
63+
CHK && checktag(T, f!, x)
6064
ydual = cfg.duals
6165
seed!(ydual, y)
6266
f!(ydual, Dual{T}(x, one(x)))

src/gradient.jl

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
###############
44

55
"""
6-
ForwardDiff.gradient(f, x::AbstractArray, cfg::GradientConfig = GradientConfig(f, x))
6+
ForwardDiff.gradient(f, x::AbstractArray, cfg::GradientConfig = GradientConfig(f, x), check=Val{true}())
77
88
Return `∇f` evaluated at `x`, assuming `f` is called as `f(x)`.
99
1010
This method assumes that `isa(f(x), Real)`.
11+
12+
Set `check` to `Val{false}()` to disable tag checking. This can lead to perturbation confusion, so should be used with care.
1113
"""
12-
function gradient(f, x::AbstractArray, cfg::GradientConfig{T} = GradientConfig(f, x)) where {T}
13-
checktag(T, f, x)
14+
function gradient(f, x::AbstractArray, cfg::GradientConfig{T} = GradientConfig(f, x), ::Val{CHK}=Val{true}()) where {T, CHK}
15+
CHK && checktag(T, f, x)
1416
if chunksize(cfg) == length(x)
1517
return vector_mode_gradient(f, x, cfg)
1618
else
@@ -19,15 +21,16 @@ function gradient(f, x::AbstractArray, cfg::GradientConfig{T} = GradientConfig(f
1921
end
2022

2123
"""
22-
ForwardDiff.gradient!(result::Union{AbstractArray,DiffResult}, f, x::AbstractArray, cfg::GradientConfig = GradientConfig(f, x))
24+
ForwardDiff.gradient!(result::Union{AbstractArray,DiffResult}, f, x::AbstractArray, cfg::GradientConfig = GradientConfig(f, x), check=Val{true}())
2325
2426
Compute `∇f` evaluated at `x` and store the result(s) in `result`, assuming `f` is called as
2527
`f(x)`.
2628
2729
This method assumes that `isa(f(x), Real)`.
30+
2831
"""
29-
function gradient!(result::Union{AbstractArray,DiffResult}, f, x::AbstractArray, cfg::GradientConfig{T} = GradientConfig(f, x)) where {T}
30-
checktag(T, f, x)
32+
function gradient!(result::Union{AbstractArray,DiffResult}, f, x::AbstractArray, cfg::GradientConfig{T} = GradientConfig(f, x), ::Val{CHK}=Val{true}()) where {T, CHK}
33+
CHK && checktag(T, f, x)
3134
if chunksize(cfg) == length(x)
3235
vector_mode_gradient!(result, f, x, cfg)
3336
else

src/hessian.jl

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,55 @@
33
###############
44

55
"""
6-
ForwardDiff.hessian(f, x::AbstractArray, cfg::HessianConfig = HessianConfig(f, x))
6+
ForwardDiff.hessian(f, x::AbstractArray, cfg::HessianConfig = HessianConfig(f, x), check=Val{true}())
77
88
Return `H(f)` (i.e. `J(∇(f))`) evaluated at `x`, assuming `f` is called as `f(x)`.
99
1010
This method assumes that `isa(f(x), Real)`.
11+
12+
Set `check` to `Val{false}()` to disable tag checking. This can lead to perturbation confusion, so should be used with care.
1113
"""
12-
function hessian(f, x::AbstractArray, cfg::HessianConfig = HessianConfig(f, x))
13-
∇f = y -> gradient(f, y, cfg.gradient_config)
14-
return jacobian(∇f, x, cfg.jacobian_config)
14+
function hessian(f, x::AbstractArray, cfg::HessianConfig{T} = HessianConfig(f, x), ::Val{CHK}=Val{true}()) where {T,CHK}
15+
CHK && checktag(T, f, x)
16+
∇f = y -> gradient(f, y, cfg.gradient_config, Val{false}())
17+
return jacobian(∇f, x, cfg.jacobian_config, Val{false}())
1518
end
1619

1720
"""
18-
ForwardDiff.hessian!(result::AbstractArray, f, x::AbstractArray, cfg::HessianConfig = HessianConfig(f, x))
21+
ForwardDiff.hessian!(result::AbstractArray, f, x::AbstractArray, cfg::HessianConfig = HessianConfig(f, x), check=Val{true}())
1922
2023
Compute `H(f)` (i.e. `J(∇(f))`) evaluated at `x` and store the result(s) in `result`,
2124
assuming `f` is called as `f(x)`.
2225
2326
This method assumes that `isa(f(x), Real)`.
27+
28+
Set `check` to `Val{false}()` to disable tag checking. This can lead to perturbation confusion, so should be used with care.
2429
"""
25-
function hessian!(result::AbstractArray, f, x::AbstractArray, cfg::HessianConfig = HessianConfig(f, x))
26-
∇f = y -> gradient(f, y, cfg.gradient_config)
27-
jacobian!(result, ∇f, x, cfg.jacobian_config)
30+
function hessian!(result::AbstractArray, f, x::AbstractArray, cfg::HessianConfig{T} = HessianConfig(f, x), ::Val{CHK}=Val{true}()) where {T,CHK}
31+
CHK && checktag(T, f, x)
32+
∇f = y -> gradient(f, y, cfg.gradient_config, Val{false}())
33+
jacobian!(result, ∇f, x, cfg.jacobian_config, Val{false}())
2834
return result
2935
end
3036

3137
"""
32-
ForwardDiff.hessian!(result::DiffResult, f, x::AbstractArray, cfg::HessianConfig = HessianConfig(f, result, x))
38+
ForwardDiff.hessian!(result::DiffResult, f, x::AbstractArray, cfg::HessianConfig = HessianConfig(f, result, x), check=Val{true}())
3339
3440
Exactly like `ForwardDiff.hessian!(result::AbstractArray, f, x::AbstractArray, cfg::HessianConfig)`, but
3541
because `isa(result, DiffResult)`, `cfg` is constructed as `HessianConfig(f, result, x)` instead of
3642
`HessianConfig(f, x)`.
43+
44+
Set `check` to `Val{false}()` to disable tag checking. This can lead to perturbation confusion, so should be used with care.
3745
"""
38-
function hessian!(result::DiffResult, f, x::AbstractArray, cfg::HessianConfig = HessianConfig(f, result, x))
46+
function hessian!(result::DiffResult, f, x::AbstractArray, cfg::HessianConfig{T} = HessianConfig(f, result, x), ::Val{CHK}=Val{true}()) where {T,CHK}
47+
CHK && checktag(T, f, x)
3948
∇f! = (y, z) -> begin
4049
inner_result = DiffResult(zero(eltype(y)), y)
41-
gradient!(inner_result, f, z, cfg.gradient_config)
50+
gradient!(inner_result, f, z, cfg.gradient_config, Val{false}())
4251
result = DiffResults.value!(result, value(DiffResults.value(inner_result)))
4352
return y
4453
end
45-
jacobian!(DiffResults.hessian(result), ∇f!, DiffResults.gradient(result), x, cfg.jacobian_config)
54+
jacobian!(DiffResults.hessian(result), ∇f!, DiffResults.gradient(result), x, cfg.jacobian_config, Val{false}())
4655
return result
4756
end
4857

@@ -57,14 +66,13 @@ hessian!(result::MutableDiffResult, f, x::SArray) = hessian!(result, f, x, Hessi
5766
hessian!(result::ImmutableDiffResult, f, x::SArray, cfg::HessianConfig) = hessian!(result, f, x)
5867

5968
function hessian!(result::ImmutableDiffResult, f::F, x::SArray{S,V}) where {F,S,V}
60-
TJ = typeof(Tag((f,gradient),V))
61-
d1 = dualize(TJ, x)
62-
TG = typeof(Tag(f, eltype(d1)))
63-
d2 = dualize(TG, d1)
69+
T = typeof(Tag(f,V))
70+
d1 = dualize(T, x)
71+
d2 = dualize(T, d1)
6472
fd2 = f(d2)
65-
val = value(TJ,value(TG,fd2))
66-
grad = extract_gradient(TJ,value(TG,fd2), x)
67-
hess = extract_jacobian(TJ,partials(TG,fd2), x)
73+
val = value(T,value(T,fd2))
74+
grad = extract_gradient(T,value(T,fd2), x)
75+
hess = extract_jacobian(T,partials(T,fd2), x)
6876
result = DiffResults.hessian!(result, hess)
6977
result = DiffResults.gradient!(result, grad)
7078
result = DiffResults.value!(result, val)

src/jacobian.jl

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
###############
44

55
"""
6-
ForwardDiff.jacobian(f, x::AbstractArray, cfg::JacobianConfig = JacobianConfig(f, x))
6+
ForwardDiff.jacobian(f, x::AbstractArray, cfg::JacobianConfig = JacobianConfig(f, x), check=Val{true}())
77
88
Return `J(f)` evaluated at `x`, assuming `f` is called as `f(x)`.
99
1010
This method assumes that `isa(f(x), AbstractArray)`.
11+
12+
Set `check` to `Val{false}()` to disable tag checking. This can lead to perturbation confusion, so should be used with care.
1113
"""
12-
function jacobian(f, x::AbstractArray, cfg::JacobianConfig{T} = JacobianConfig(f, x)) where {T}
13-
checktag(T, f, x)
14+
function jacobian(f, x::AbstractArray, cfg::JacobianConfig{T} = JacobianConfig(f, x), ::Val{CHK}=Val{true}()) where {T,CHK}
15+
CHK && checktag(T, f, x)
1416
if chunksize(cfg) == length(x)
1517
return vector_mode_jacobian(f, x, cfg)
1618
else
@@ -19,13 +21,15 @@ function jacobian(f, x::AbstractArray, cfg::JacobianConfig{T} = JacobianConfig(f
1921
end
2022

2123
"""
22-
ForwardDiff.jacobian(f!, y::AbstractArray, x::AbstractArray, cfg::JacobianConfig = JacobianConfig(f!, y, x))
24+
ForwardDiff.jacobian(f!, y::AbstractArray, x::AbstractArray, cfg::JacobianConfig = JacobianConfig(f!, y, x), check=Val{true}())
2325
2426
Return `J(f!)` evaluated at `x`, assuming `f!` is called as `f!(y, x)` where the result is
2527
stored in `y`.
28+
29+
Set `check` to `Val{false}()` to disable tag checking. This can lead to perturbation confusion, so should be used with care.
2630
"""
27-
function jacobian(f!, y::AbstractArray, x::AbstractArray, cfg::JacobianConfig{T} = JacobianConfig(f!, y, x)) where {T}
28-
checktag(T, f!, x)
31+
function jacobian(f!, y::AbstractArray, x::AbstractArray, cfg::JacobianConfig{T} = JacobianConfig(f!, y, x), ::Val{CHK}=Val{true}()) where {T, CHK}
32+
CHK && checktag(T, f!, x)
2933
if chunksize(cfg) == length(x)
3034
return vector_mode_jacobian(f!, y, x, cfg)
3135
else
@@ -35,15 +39,17 @@ end
3539

3640

3741
"""
38-
ForwardDiff.jacobian!(result::Union{AbstractArray,DiffResult}, f, x::AbstractArray, cfg::JacobianConfig = JacobianConfig(f, x))
42+
ForwardDiff.jacobian!(result::Union{AbstractArray,DiffResult}, f, x::AbstractArray, cfg::JacobianConfig = JacobianConfig(f, x), check=Val{true}())
3943
4044
Compute `J(f)` evaluated at `x` and store the result(s) in `result`, assuming `f` is called
4145
as `f(x)`.
4246
4347
This method assumes that `isa(f(x), AbstractArray)`.
48+
49+
Set `check` to `Val{false}()` to disable tag checking. This can lead to perturbation confusion, so should be used with care.
4450
"""
45-
function jacobian!(result::Union{AbstractArray,DiffResult}, f, x::AbstractArray, cfg::JacobianConfig{T} = JacobianConfig(f, x)) where {T}
46-
checktag(T, f, x)
51+
function jacobian!(result::Union{AbstractArray,DiffResult}, f, x::AbstractArray, cfg::JacobianConfig{T} = JacobianConfig(f, x), ::Val{CHK}=Val{true}()) where {T, CHK}
52+
CHK && checktag(T, f, x)
4753
if chunksize(cfg) == length(x)
4854
vector_mode_jacobian!(result, f, x, cfg)
4955
else
@@ -53,15 +59,17 @@ function jacobian!(result::Union{AbstractArray,DiffResult}, f, x::AbstractArray,
5359
end
5460

5561
"""
56-
ForwardDiff.jacobian!(result::Union{AbstractArray,DiffResult}, f!, y::AbstractArray, x::AbstractArray, cfg::JacobianConfig = JacobianConfig(f!, y, x))
62+
ForwardDiff.jacobian!(result::Union{AbstractArray,DiffResult}, f!, y::AbstractArray, x::AbstractArray, cfg::JacobianConfig = JacobianConfig(f!, y, x), check=Val{true}())
5763
5864
Compute `J(f!)` evaluated at `x` and store the result(s) in `result`, assuming `f!` is
5965
called as `f!(y, x)` where the result is stored in `y`.
6066
6167
This method assumes that `isa(f(x), AbstractArray)`.
68+
69+
Set `check` to `Val{false}()` to disable tag checking. This can lead to perturbation confusion, so should be used with care.
6270
"""
63-
function jacobian!(result::Union{AbstractArray,DiffResult}, f!, y::AbstractArray, x::AbstractArray, cfg::JacobianConfig{T} = JacobianConfig(f!, y, x)) where {T}
64-
checktag(T, f!, x)
71+
function jacobian!(result::Union{AbstractArray,DiffResult}, f!, y::AbstractArray, x::AbstractArray, cfg::JacobianConfig{T} = JacobianConfig(f!, y, x), ::Val{CHK}=Val{true}()) where {T,CHK}
72+
CHK && checktag(T, f!, x)
6573
if chunksize(cfg) == length(x)
6674
vector_mode_jacobian!(result, f!, y, x, cfg)
6775
else

test/GradientTest.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ for c in (1, 2, 3), tag in (nothing, Tag(f, eltype(x)))
4646
@test isapprox(DiffResults.value(out), v)
4747
end
4848

49+
cfgx = ForwardDiff.GradientConfig(sin, x)
50+
@test_throws ForwardDiff.InvalidTagException ForwardDiff.gradient(f, x, cfgx)
51+
@test ForwardDiff.gradient(f, x, cfgx, Val{false}()) == ForwardDiff.gradient(f,x)
52+
53+
4954
########################
5055
# test vs. Calculus.jl #
5156
########################
@@ -130,4 +135,5 @@ sresult3 = ForwardDiff.gradient!(sresult3, prod, sx, scfg)
130135
@test DiffResults.gradient(sresult2) == DiffResults.gradient(result)
131136
@test DiffResults.gradient(sresult3) == DiffResults.gradient(result)
132137

138+
133139
end # module

0 commit comments

Comments
 (0)