ModelingToolkit variable with Distributions.cdf()

Hi,

I am new to Julia and trying out the ModelingToolking package. I have a basic understanding of the type system and the dynamic dispatch, but couldn’t figure out why this didn’t work.

This works perfectly - almost magic to me:

>>> using ModelingToolkit
>>> using Distributions
>>> @variables t
>>> d = Normal()
>>> pdf(d, t)
(exp((-abs2((t - 0.0) / 1.0)) / 2) * invsqrt2π) / 1.0

Then this didn’t work.

>>> cdf(d, t)
MethodError: no method matching AbstractFloat(::Num)
Closest candidates are:
  AbstractFloat(::Real, !Matched::RoundingMode) where T<:AbstractFloat at rounding.jl:200
  AbstractFloat(::T) where T<:Number at boot.jl:716
  AbstractFloat(!Matched::Bool) at float.jl:258

If I check the Distributions package documentation, both functions have the same function signature:

cdf(d::UnivariateDistribution, x::Real)
pdf(d::UnivariateDistribution, x::Real)

Also, from the ModelingToolkit tutorial,

Note that by default, @variables returns Sym or Term objects wrapped in Num in order to make them behave like subtypes of Real .

So, I’m wondering why it worked for pdf but not for cdf.

1 Like

Symbolic values get passed through normal Julia code fine, but in this case erfc is implemented in C and Julia attempts to convert the symbolic value t to a floating point number to pass to the C routine. This can’t work, so you get the traceback. The solution is to tell ModelingToolkit that erfc is a fundamental operation and that it shouldn’nt bother trying to look inside it.

julia> using SpecialFunctions
julia> @register SpecialFunctions.erfc(x)
julia> cdf(d, t)
erfc((-((t - 0.0) / 1.0)) * invsqrt2) / 2

In the case of pdf, exp had already been registered with ModelingToolkit, so it already knew not to look inside.

2 Likes

I kinda remember that Matlab can symbolically differentiate its normcdf function. Does that mean Matlab’s normcdf is not derived from erfc?

I encountered this same issue two years but still haven’t figured out how to do it in Julia…

Thanks, @contradict. It’s great to know that this works!

@register SpecialFunctions.erfc(x)
@variables t
@derivatives G'~t 
Φ = cdf(d, t)
expand_derivatives(G(Φ))
# 0.3989422804014327 * exp(-0.5000000000000001 * (t ^ 2))

Oh, yikes, I’m surprised that worked! For a function like erfc that is implemented in a C library, you also need to register a derivative if you want to differentiate it. It looks like a derivative was registered for erfc, but the function itself was not registered, and I’m not sure where it should have been.

I don’t know how Matlab handles this, but ModelingToolkit defines the derivatives of some base set of functions and then works to express derivatives of your expression in terms of that base set.

1 Like