AD vs argument lists

To enable AD I must do something about argument types. Let us say I have a function like this:

add_btdb_ut_only!(Ke::Matrix{T}, B::Matrix{T}, Jac_w::T, D::Matrix{T}, DB::Matrix{T}) where {T}

I might be able to guess which arguments will need to be typed for ForwardDiff.Dual use, or I might not.

What is a good way of dealing with this situation?

Relax the types? Why do the elements all need to be the same? Why do they need to be Matrix and not AbstractMatrix?

So, four matrix subtypes of AbstractMatrix + plus one scalar type?
Or, should I just drop all types? function add_btdb_ut_only!(Ke, B, Jac_w::T, D, DB) where {T<:Real}…
The latter seems appealing. Dual is a subtype of Real, so this should work for all possible combinations of inputs, I think.

I mean just:

add_btdb_ut_only!(Ke::AbstractMatrix, B::AbstractMatrix, Jac_w::Number, D::AbstractMatrix, DB::AbstractMatrix)

Why do you need a where clause at all? If you need to enforce that they are all Real, do:

add_btdb_ut_only!(Ke::AbstractMatrix{<:Real}, B::AbstractMatrix{<:Real}, Jac_w::Real, D::{<:Real}, DB::{<:Real})

That’s always an option: just duck-type.

1 Like

But now, with duck typing, I should probably document the types of the arguments in the doc string. Previously the signature served as a documentation…

Well, you can always do add_btdb_ut_only!(Ke::AbstractMatrix{<:Real}, B::AbstractMatrix{<:Real}, Jac_w::Real, D::{<:Real}, DB::{<:Real}). The point is that the previous signature was simply narrower than (presumably) the function required.

Note, however, that if you want to support arbitrary types like this, you should be more careful in implementing the function too. e.g. if you need to allocate an array within the function, you should compute its type from the arguments rather than simply assuming it should be Float64.
Type-generic programming takes a bit of practice, but is worth it in the long run.

2 Likes