Help making ForwardDiff.derivative work

Hi there,

I define my custom parametric types as follows:

struct Parameters{T}
    """ 
    inmutable type to pack the parameters estimated.
    """
    ϕ::T 
    λ::T 
    γ₁::T
end

struct MarketPostEstimation{T}
    """ 
    inmutable type to pack the data from each market after estimation.
    """
    s::T 
    p::T 
    x::T 
    z::T
    ξ::T
    ω::T
end

parameters = Parameters(rand(3)...)
market_1 = MarketPostEstimation(rand(6)...)

Then I define the following functions:

function s_func(p, market; β₀ = 1.5, β₁ = 0.5, α = -0.5)
    """
    function to compute theoretical market shares 
    inputs:
        1) p -> price_vector
        2) x -> product characteristic 
        3) ξ -> unobserved demand shock
    outputs:
        1) market share vector 
    """
    δ = β₀ + β₁ * market.x + market.ξ
    temp = map(pₖ -> exp(δ + α * pₖ), p)
    denominator = 1 + sum(temp)
    return temp ./ denominator
end

function solve_price_eq_duopoly(τ, market, parameters; α = - 0.5)
    """
    solve for the equilibrium prices for a given (τ₁, τ₂) by first solving for the "markup" and then computing the prices.
    inputs:
        1) τ tuple 
        2) market 
        3) parameter instance
    outputs:
        1) optimal (p₁, p₂) 
    """
    initial_guess = 0.0
    Δ = find_zero(Δ -> 1 + α * (Δ - parameters.λ)*(1 - sum(s_func((τ[1] + Δ, τ[2] + Δ), market))), initial_guess)
    return τ .+ Δ
end

p_1_star(τ₁, τ₂, market, parameters) = solve_price_eq_duopoly((τ₁, τ₂), market, parameters)[1]
p_2_star(τ₁, τ₂, market, parameters) = solve_price_eq_duopoly((τ₁, τ₂), market, parameters)[2]

and my objective is to be able to use ForwardDiff.derivative on those functions like:

ForwardDiff.derivative(x -> p_1_star(x, 3.4, market_1, parameters), 4.5)

but I get the following error:

ERROR: MethodError: no method matching Float64(::ForwardDiff.Dual{ForwardDiff.Tag{var"#67#68", Float64}, Float64, 1})
The type `Float64` exists, but no method is defined for this combination of argument types when trying to construct it.

Closest candidates are:
  (::Type{T})(::Real, ::RoundingMode) where T<:AbstractFloat
   @ Base rounding.jl:265
  (::Type{T})(::T) where T<:Number
   @ Core boot.jl:900
  Float64(::IrrationalConstants.Logten)
   @ IrrationalConstants ~/.julia/packages/IrrationalConstants/vp5v4/src/macro.jl:112
  ...

Stacktrace:
  [1] convert(::Type{Float64}, x::ForwardDiff.Dual{ForwardDiff.Tag{var"#67#68", Float64}, Float64, 1})
    @ Base ./number.jl:7
  [2] update_state
    @ ~/.julia/packages/Roots/E1WQf/src/DerivativeFree/secant.jl:38 [inlined]
  [3] update_state
    @ ~/.julia/packages/Roots/E1WQf/src/DerivativeFree/secant.jl:29 [inlined]
  [4] solve!(𝐙::Roots.ZeroProblemIterator{Secant, AlefeldPotraShi, Roots.Callable_Function{…}, Roots.UnivariateZeroState{…}, Roots.UnivariateZeroOptions{…}, Roots.NullTracks}; verbose::Bool)
    @ Roots ~/.julia/packages/Roots/E1WQf/src/hybrid.jl:54
  [5] solve!
    @ ~/.julia/packages/Roots/E1WQf/src/hybrid.jl:30 [inlined]
  [6] solve(𝑭𝑿::ZeroProblem{var"#12#13"{Float64, Tuple{…}, MarketPostEstimation{…}, Parameters{…}}, Float64}, M::Order0, p::Nothing; verbose::Bool, kwargs::@Kwargs{tracks::Roots.NullTracks})
    @ Roots ~/.julia/packages/Roots/E1WQf/src/find_zero.jl:492
  [7] find_zero(f::Function, x0::Float64, M::Order0, p′::Nothing; p::Nothing, verbose::Bool, tracks::Roots.NullTracks, kwargs::@Kwargs{})
    @ Roots ~/.julia/packages/Roots/E1WQf/src/find_zero.jl:220
  [8] find_zero (repeats 2 times)
    @ ~/.julia/packages/Roots/E1WQf/src/find_zero.jl:210 [inlined]
  [9] find_zero
    @ ~/.julia/packages/Roots/E1WQf/src/find_zero.jl:243 [inlined]
 [10] solve_price_eq_duopoly(τ::Tuple{ForwardDiff.Dual{ForwardDiff.Tag{var"#67#68", Float64}, Float64, 1}, Float64}, market::MarketPostEstimation{Float64}, parameters::Parameters{Float64}; α::Float64)
    @ Main ./REPL[13]:12
 [11] solve_price_eq_duopoly
    @ ./REPL[13]:1 [inlined]
 [12] p_1_star(τ₁::ForwardDiff.Dual{ForwardDiff.Tag{var"#67#68", Float64}, Float64, 1}, τ₂::Float64, market::MarketPostEstimation{Float64}, parameters::Parameters{Float64})
    @ Main ./REPL[57]:1
 [13] (::var"#67#68")(x::ForwardDiff.Dual{ForwardDiff.Tag{var"#67#68", Float64}, Float64, 1})
    @ Main ./REPL[99]:1
 [14] derivative(f::var"#67#68", x::Float64)
    @ ForwardDiff ~/.julia/packages/ForwardDiff/UBbGT/src/derivative.jl:14
 [15] top-level scope
    @ REPL[99]:1
Some type information was truncated. Use `show(err)` to see complete types.

It seems like my function checks out the requirements in the documentation but maybe I’m missing something?

Thanks a lot!
Miguel.

Try providing an initial guess that has the same type as the Dual number, e.g.,

initial_guess = zero(eltype(τ))

If τ is the variable that is being differentiated w.r.t.

The tells the root finder that it has to setup its internal variables using this number type, instead of Float64, the type of 0.0.

2 Likes

Aha! Thanks a lot!

1 Like