Type Instability between 0.5 and 0.6 - Type Definition


#1

Hi all, I have begun to migrate my package from 0.5 to 0.6, and I’ve noticed something interesting regarding type instability between the two. Best to show with code:
0.5

abstract ABC 

type A <: ABC 
a::Float64 
end 

type B <: ABC 
a::Float64 
end 

type C{T <: ABC} 
a::T 
end

Here’s the code_warntype for:

@code_warntype C(A(4.5))
Variables:
   #self#::Type{C}
   a::A

Body: 
  begin 
        return $(Expr(:new, C{A}, :(a))) 
  end::C{A}

Vs 0.6:

abstract type ABC end 

struct A <: ABC 
a::Float64 
end 

struct B <: ABC 
a::Float64 
end 

struct C{T <: ABC} 
a::T 
end

And the code_warntype:

Variables:
  #self#::Any 
  a::A

Body: 
   begin 
          return $(Expr(:new, :($(QuoteNode(C{A}))), :(a))) 
   end::C{A}

I’m not sure how material this might be in the end (it clearly can resolve the type at the very end), but I’ve noticed particular slow downs in my code between 0.5 and 0.6 where in 0.6 the compiler is unable to resolve certain types that it could in 0.5. Is there some other way we should be defining things?

Thanks!

Chris


#2

That Any is not used.


#3

OK, that makes sense since it was able to resolve the type in the end. I am however still seeing type instability that I don’t quite understand. I don’t see it in 0.5 and I actually don’t see it in 0.6 while still using “type” to define types (as opposed to struct/mutable struct). Here are the types in question:

mutable struct TermStructureFittingParameter{T <: TermStructure} <: Parameter
  times::Vector{Float64}
  values::Vector{Float64}
  ts::T
end

mutable struct HullWhiteDynamics{P<:Parameter} <: ShortRateDynamics{P}
  process::OrnsteinUhlenbeckProcess
  fitting::P
  a::Float64
  sigma::Float64
end

mutable struct OneFactorShortRateTree{S <: ShortRateDynamics, P <: StochasticProcess} <: ShortRateTree
  tree::TrinomialTree{P}
  dynamics::S
  tg::TimeGrid
  treeLattice::TreeLattice1D{OneFactorShortRateTree{S, P}}

  function OneFactorShortRateTree{S, P}(tree::TrinomialTree{P}, dynamics::S, tg::TimeGrid) where {S, P}
    oneFactorTree = new{S, P}(tree, dynamics, tg)
    oneFactorTree.treeLattice = TreeLattice1D(tg, get_size(tree, 2), oneFactorTree)

    return oneFactorTree
  end
end

The compiler in 0.6 with these definitions can’t seem to resolve the type of the HullWhiteDynamics in the Tree type (the last one defined). It has no problem in 0.5 or 0.6 using the “type” keyword. See here, this code_warntype usage is calling a method with the tree passed in as an argument:

Variables:
  #self#::QuantLib.#get_state_prices!
  tree::QuantLib.OneFactorShortRateTree{QuantLib.HullWhiteDynamics,QuantLib.OrnsteinUhlenbeckProcess}
  i::Int64

vs:

Variables:
  #self#::QuantLib.#get_state_prices!
  tree::QuantLib.OneFactorShortRateTree{QuantLib.HullWhiteDynamics{QuantLib.TermStructureFittingParameter{QuantLib.FlatForwardTermStructure{QuantLib.T
ime.TargetCalendar,QuantLib.Time.Actual365,QuantLib.ContinuousCompounding,QuantLib.Time.Annual}}},QuantLib.OrnsteinUhlenbeckProcess}
  i::Int64```

As you can see in the latter output, it knows exactly what type of HullWhiteDynamics is passed in.  Is there something wrong with the way I am using struct/mutable struct and some of the new syntax?

Thanks!

Chris

#4

Impossible to tell without full code.


#5

OK - it’s a bit involved, but the example driving this is here (from the “main” function):

The related code is here:





#6

Specifically, a method called “tree” gets called in hull_white.jl (line 42), which is where this tree object is created:

numericTree = OneFactorShortRateTree{HullWhiteDynamics, typeof(numericDynamics.process)}(trinomial, numericDynamics, grid)

In 0.5, when I call this method, it’s able to resolve all the types associated with this obj:

 get_state_prices!(numericTree, i)```

In 0.6, it can't.

#7

Just thought I’d update - it seems in 0.5 it was able to resolve just passing in HullWhiteDynamics as the type parameter, but not in 0.6. So I had to call typeof to pass in the exact type. Much better now.