How can I fix the type instability

Hello,

I have the following MWE, where I cannot remove the type instability that appears from the call on the last line.

abstract type FrictionLaw end

struct Cubic{T <: Real} <: FrictionLaw
    γ::T
end

g(x, law::Cubic) = @. law.γ * x^3

function finite_diff_jac(f, x)
    xᵢ₊₁ = similar(x)
    fᵢ = f(x)
    fᵢ₊₁ = similar(fᵢ)
    J = similar(x, length(fᵢ), length(x))

    h = 1e-8
    for j in eachindex(x)
        xᵢ₊₁ .= x
        xᵢ₊₁[j] += h
        fᵢ₊₁ .= f(xᵢ₊₁)
        @. J[:, j] = (fᵢ₊₁ - fᵢ) / h
    end

    return J
end

function perform_aft(E, Eᴴ, X, friction_law)
    return Eᴴ * g(E * X, friction_law)
end

function friction_force_fourier(Xⱼ, xₚ, gₚ, friction_law, E, Eᴴ)
    Gⱼ = perform_aft(E, Eᴴ, [Xⱼ[1] + 2xₚ; Xⱼ[2:end]], friction_law)
    Gⱼ[1] -= 2gₚ
    return Gⱼ
end

H = 10

@code_warntype finite_diff_jac(u -> friction_force_fourier(u,
        0.1,
        0.1,
        Cubic(1.0),
        rand(2H + 1, 2H + 1),
        rand(2H + 1, 2H + 1)),
    ones(2H + 1))

Upon executing the code, I get

MethodInstance for finite_diff_jac(::var"#17#18", ::Vector{Float64})
  from finite_diff_jac(f, x) @ Main ~/ITP_Friction/HBMContinuation.jl/scripts/new/mwe_typeinst.jl:9
Arguments
  #self#::Core.Const(finite_diff_jac)
  f::Core.Const(var"#17#18"())
  x::Vector{Float64}
Locals
  @_4::Union{Nothing, Tuple{Int64, Int64}}
  h::Float64
  J::Union{Vector, Matrix{Float64}}
  fᵢ₊₁::Any
  fᵢ::Any
  xᵢ₊₁::Vector{Float64}
  j::Int64
Body::Union{Vector, Matrix{Float64}}

where the function vectors are of type Any, although I do not understand where this instability is coming from. Help will be appreaciated.

Avoid untyped global variables. H is used in the anonymous function, where the type instability then appears. I guess you can fix this by either adding a const when defining H, or adding a let H = H, ... around the definition of the anonymous function.

1 Like

While trying this @nsajko already posted the correct answer. Here is a REPL output to showcase the problem. When you just call the anonymous function like you defined here, nothing can be inferred because it captures the untyped global variable H

julia> @code_warntype (u -> friction_force_fourier(u,
               0.1,
               0.1,
               Cubic(1.0),
               rand(2H + 1, 2H + 1),
               rand(2H + 1, 2H + 1)))(
           ones(2H + 1))
MethodInstance for (::var"#6#7")(::Vector{Float64})
  from (::var"#6#7")(u) @ Main REPL[11]:1
Arguments
  #self#::Core.Const(var"#6#7"())
  u::Vector{Float64}
Body::Any
1 ─ %1  = Main.Cubic(1.0)::Core.Const(Cubic{Float64}(1.0))
│   %2  = (2 * Main.H)::Any
│   %3  = (%2 + 1)::Any
│   %4  = (2 * Main.H)::Any
│   %5  = (%4 + 1)::Any
│   %6  = Main.rand(%3, %5)::Any
│   %7  = (2 * Main.H)::Any
│   %8  = (%7 + 1)::Any
│   %9  = (2 * Main.H)::Any
│   %10 = (%9 + 1)::Any
│   %11 = Main.rand(%8, %10)::Any
│   %12 = Main.friction_force_fourier(u, 0.1, 0.1, %1, %6, %11)::Any
└──       return %12

So we can change this by surrounding it with let H=H like so:

fun = let H=H
        u -> friction_force_fourier(u,
               0.1,
               0.1,
               Cubic(1.0),
               rand(2H + 1, 2H + 1),
               rand(2H + 1, 2H + 1))
       end

This effectively converts the global H into a “local” H for the definition. So now everything is type-stable:

julia> @code_warntype fun(ones(2H + 1))
MethodInstance for (::var"#8#9"{Int64})(::Vector{Float64})
  from (::var"#8#9")(u) @ Main REPL[14]:2
Arguments
  #self#::var"#8#9"{Int64}
  u::Vector{Float64}
Body::Vector{Float64}

julia> @code_warntype finite_diff_jac(fun, ones(2H + 1))
MethodInstance for finite_diff_jac(::var"#8#9"{Int64}, ::Vector{Float64})
  from finite_diff_jac(f::F, x) where F @ Main REPL[9]:1
Static Parameters
  F = var"#8#9"{Int64}
Arguments
  #self#::Core.Const(finite_diff_jac)
  f::var"#8#9"{Int64}
  x::Vector{Float64}
Locals
  @_4::Union{Nothing, Tuple{Int64, Int64}}
  h::Float64
  J::Matrix{Float64}
  fᵢ₊₁::Vector{Float64}
  fᵢ::Vector{Float64}
  xᵢ₊₁::Vector{Float64}
  j::Int64
Body::Matrix{Float64}
1 Like

Of course. I was actually benchmarking some functions and I thought I had a type instability. It turns out that I didn’t check for global variables… Silly mistake

1 Like