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

Add seed_duals convenience and document how to seed Dual numbers #379

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 44 additions & 0 deletions docs/src/user/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,47 @@ want to disable this checking.
cfg = GradientConfig(nothing, x)
gradient(f, x, cfg)
```

## Manually Seeding Dual Numbers

In some cases you may want to manually seed the `Dual` numbers and directly
send these to a function. This can be useful for example if said function does
not return a scalar or vector but still has a useful interpretation of a derivative.

Let's say we had a vector `x=[1.0,1.5,3.0,1.0]` and wanted to take the derivative
of some algorithm with respect to each value. Since we are taking 4 different
derivatives, we will have 4 separate partials. For each partial, we seed a
`1.0` at the source of the value, and a zero everywhere else. The Dual seeded
version of this vector would thus be:

```julia
using ForwardDiff: Dual
struct MyTag end # Unique tag for the Duals
x1dual = Dual{MyTag}(1.0, (1.0, 0.0, 0.0, 0.0))
x2dual = Dual{MyTag}(1.5, (0.0, 1.0, 0.0, 0.0))
x3dual = Dual{MyTag}(3.0, (0.0, 0.0, 1.0, 0.0))
x4dual = Dual{MyTag}(1.0, (0.0, 0.0, 0.0, 1.0))
xdual = [x1dual, x2dual, x3dual, x4dual]
```

Calculations using `xdual` instead of `x` will result in `Dual` numbers and the
partial terms will be the derivative with respect to `x[i]`. Most conversions
will happen automatically, but you may need to convert other inputs into the
function to zero-seeded duals, and this can be done by simply calling
`convert(eltype(p),y)` on a scalar `y` (or broadcasting this conversion).

As a convenience, to seed a vector with unique partials for the size of the
vector you can use the `seed_duals` function. Thus the following is equivalent
to the code above:

```julia
xdual = ForwardDiff.seed_duals(x,MyTag)
```

After the calculations are done, the value and derivative of the calculation
can be extracted using the following:

```julia
y.value # Returns the value, i.e. what would've been produced without Duals
y.partials[i] # Returns the derivative of the value w.r.t. the ith variable
```
10 changes: 10 additions & 0 deletions src/dual.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ end
@inline Dual{T,V,N}(x::Number) where {T,V,N} = convert(Dual{T,V,N}, x)
@inline Dual{T,V}(x) where {T,V} = convert(Dual{T,V}, x)

############################
# Convenience Constructors #
############################

function seed_duals(x::AbstractArray{V},::Type{T},
::Chunk{N} = Chunk(x)) where {V,T,N}
seeds = construct_seeds(Partials{N,V})
duals = [Dual{T}(x[i],seeds[i]) for i in eachindex(x)]
end

##############################
# Utility/Accessor Functions #
##############################
Expand Down