Solving type instability when using Graphs.jl

This is the flame plot I get when profiling my iterative solver.
You can see it’s pointed by tiny red blocks, which I’m trying to address one by one.

In particular I’m now looking at the one highlighted by the red arrow, occurring at `solver.jl, solve!: line 51`.

Here's the full `solve!` function
``````function solve!(n::Network, dt::Float64, current_time::Float64)::Nothing
for edge in n.edges
s = Graphs.src(edge)
t = Graphs.dst(edge)

s == 1 && set_inlet_bc(current_time, dt, n.vessels[(s, t)], n.heart)

# TODO: multiple inlets

# vessel
muscl!(n.vessels[(s, t)], dt, n.blood)

# downstream
outdeg::Int64 = Graphs.outdegree(n.graph, t)
if outdeg == 0 # outlet
set_outlet_bc(dt, n.vessels[(s, t)], n.blood.rho)
elseif outdeg == 1
indeg::Int64 = Graphs.indegree(n.graph, t)
d::Int64 = first(Graphs.outneighbors(n.graph, t))
if indeg == 1 # conjunction
join_vessels!(n.blood, n.vessels[(s, t)], n.vessels[(t, d)])
# TODO: test ANASTOMOSIS!!!
elseif indeg == 2 # anastomosis
ps::Vector{Int64} = Graphs.inneighbors(n.graph, t)
if t == max(ps[1], ps[2])
solveAnastomosis(
n.vessels[(ps[1], t)],
n.vessels[(ps[2], t)],
n.vessels[(t, d)],
)
end
end
elseif outdeg == 2 # bifurcation
ds::Vector{Int64} = Graphs.outneighbors(n.graph, t)
join_vessels!(n, n.vessels[(s, t)], n.vessels[t, ds[1]], n.vessels[t, ds[2]])
end
end
end
``````

and this is the `Network` struct in case it is useful

``````struct Network
graph::SimpleDiGraph
edges::Vector{Graphs.SimpleGraphs.SimpleEdge{Int64}}
vessels::Dict{Tuple{Int,Int},Vessel}
blood::Blood
heart::Heart
Ccfl::Float64
bifU::MVector{6, Float64}
bifW::MVector{3, Float64}
bifF::MVector{6, Float64}
bifJ::MArray{Tuple{6, 6}, Float64, 2, 36}
end
``````

the relevant line is

``````outdeg::Int64 = Graphs.outdegree(n.graph, t)
``````

when clicking on that block and calling `warntype_clicked()` I get the following

`warntype_clicked()` result
``````MethodInstance for openBF.solve!(::openBF.Network, ::Float64, ::Float64)
from solve!(n::openBF.Network, dt::Float64, current_time::Float64) @ openBF ~/openBF/src/solver.jl:37
Arguments
#self#::Core.Const(openBF.solve!)
n::openBF.Network
dt::Float64
current_time::Float64
Locals
@_5::Union{Nothing, Tuple{Graphs.SimpleGraphs.SimpleEdge{Int64}, Int64}}
edge::Graphs.SimpleGraphs.SimpleEdge{Int64}
ds::Vector{Int64}
ps::Vector{Int64}
d::Int64
indeg::Int64
outdeg::Int64
t::Int64
s::Int64
@_14::Int64
@_15::Int64
@_16::Integer
@_17::Vector{T} where T<:Integer
@_18::Vector{T} where T<:Integer
@_19::Nothing
Body::Nothing
1 ── %1   = openBF.Nothing::Core.Const(Nothing)
│    %2   = Base.getproperty(n, :edges)::Vector{Graphs.SimpleGraphs.SimpleEdge{Int64}}
│           (@_5 = Base.iterate(%2))
│    %4   = (@_5 === nothing)::Bool
│    %5   = Base.not_int(%4)::Bool
└───        goto #32 if not %5
2 ┄─        Core.NewvarNode(:(ds))
│           Core.NewvarNode(:(ps))
│           Core.NewvarNode(:(d))
│           Core.NewvarNode(:(indeg))
│           Core.NewvarNode(:(outdeg))
│    %12  = @_5::Tuple{Graphs.SimpleGraphs.SimpleEdge{Int64}, Int64}
│           (edge = Core.getfield(%12, 1))
│    %14  = Core.getfield(%12, 2)::Int64
│    %15  = Graphs.src::Core.Const(Graphs.src)
│           (s = (%15)(edge))
│    %17  = Graphs.dst::Core.Const(Graphs.dst)
│           (t = (%17)(edge))
│    %19  = (s == 1)::Bool
└───        goto #4 if not %19
3 ── %21  = Base.getproperty(n, :vessels)::Dict{Tuple{Int64, Int64}, openBF.Vessel}
│    %22  = Core.tuple(s, t)::Tuple{Int64, Int64}
│    %23  = Base.getindex(%21, %22)::openBF.Vessel
│    %24  = Base.getproperty(n, :heart)::openBF.Heart
│           openBF.set_inlet_bc(current_time, dt, %23, %24)
└───        goto #4
4 ┄─ %27  = Base.getproperty(n, :vessels)::Dict{Tuple{Int64, Int64}, openBF.Vessel}
│    %28  = Core.tuple(s, t)::Tuple{Int64, Int64}
│    %29  = Base.getindex(%27, %28)::openBF.Vessel
│    %30  = Base.getproperty(n, :blood)::openBF.Blood
│           openBF.muscl!(%29, dt, %30)
│    %32  = Graphs.outdegree::Core.Const(Graphs.outdegree)
│    %33  = Base.getproperty(n, :graph)::Graphs.SimpleGraphs.SimpleDiGraph
│    %34  = (%32)(%33, t)::Int64
│           (@_14 = %34)
│    %36  = (@_14 isa openBF.Int64)::Core.Const(true)
└───        goto #6 if not %36
5 ──        goto #7
6 ──        Core.Const(:(Base.convert(openBF.Int64, @_14)))
└───        Core.Const(:(@_14 = Core.typeassert(%39, openBF.Int64)))
7 ┄─        (outdeg = @_14)
│    %42  = (outdeg == 0)::Bool
└───        goto #9 if not %42
8 ── %44  = Base.getproperty(n, :vessels)::Dict{Tuple{Int64, Int64}, openBF.Vessel}
│    %45  = Core.tuple(s, t)::Tuple{Int64, Int64}
│    %46  = Base.getindex(%44, %45)::openBF.Vessel
│    %47  = Base.getproperty(n, :blood)::openBF.Blood
│    %48  = Base.getproperty(%47, :rho)::Float64
│           openBF.set_outlet_bc(dt, %46, %48)
└───        goto #30
9 ── %51  = (outdeg == 1)::Bool
└───        goto #25 if not %51
10 ─ %53  = Graphs.indegree::Core.Const(Graphs.indegree)
│    %54  = Base.getproperty(n, :graph)::Graphs.SimpleGraphs.SimpleDiGraph
│    %55  = (%53)(%54, t)::Int64
│           (@_15 = %55)
│    %57  = (@_15 isa openBF.Int64)::Core.Const(true)
└───        goto #12 if not %57
11 ─        goto #13
12 ─        Core.Const(:(Base.convert(openBF.Int64, @_15)))
└───        Core.Const(:(@_15 = Core.typeassert(%60, openBF.Int64)))
13 ┄        (indeg = @_15)
│    %63  = Graphs.outneighbors::Core.Const(Graphs.outneighbors)
│    %64  = Base.getproperty(n, :graph)::Graphs.SimpleGraphs.SimpleDiGraph
│    %65  = (%63)(%64, t)::Vector{T} where T<:Integer
│    %66  = openBF.first(%65)::Integer
│           (@_16 = %66)
│    %68  = (@_16 isa openBF.Int64)::Bool
└───        goto #15 if not %68
14 ─        goto #16
15 ─ %71  = Base.convert(openBF.Int64, @_16)::Int64
└───        (@_16 = Core.typeassert(%71, openBF.Int64))
16 ┄        (d = @_16::Int64)
│    %74  = (indeg == 1)::Bool
└───        goto #18 if not %74
17 ─ %76  = Base.getproperty(n, :blood)::openBF.Blood
│    %77  = Base.getproperty(n, :vessels)::Dict{Tuple{Int64, Int64}, openBF.Vessel}
│    %78  = Core.tuple(s, t)::Tuple{Int64, Int64}
│    %79  = Base.getindex(%77, %78)::openBF.Vessel
│    %80  = Base.getproperty(n, :vessels)::Dict{Tuple{Int64, Int64}, openBF.Vessel}
│    %81  = Core.tuple(t, d)::Tuple{Int64, Int64}
│    %82  = Base.getindex(%80, %81)::openBF.Vessel
│           openBF.join_vessels!(%76, %79, %82)
└───        goto #24
18 ─ %85  = (indeg == 2)::Bool
└───        goto #24 if not %85
19 ─ %87  = Graphs.inneighbors::Core.Const(Graphs.inneighbors)
│    %88  = Base.getproperty(n, :graph)::Graphs.SimpleGraphs.SimpleDiGraph
│    %89  = (%87)(%88, t)::Vector{T} where T<:Integer
│    %90  = Core.apply_type(openBF.Vector, openBF.Int64)::Core.Const(Vector{Int64})
│           (@_17 = %89)
│    %92  = (@_17 isa %90)::Bool
└───        goto #21 if not %92
20 ─        goto #22
21 ─ %95  = Base.convert(%90, @_17)::Vector{Int64}
└───        (@_17 = Core.typeassert(%95, %90))
22 ┄        (ps = @_17::Vector{Int64})
│    %98  = t::Int64
│    %99  = Base.getindex(ps, 1)::Int64
│    %100 = Base.getindex(ps, 2)::Int64
│    %101 = openBF.max(%99, %100)::Int64
│    %102 = (%98 == %101)::Bool
└───        goto #24 if not %102
23 ─ %104 = Base.getproperty(n, :vessels)::Dict{Tuple{Int64, Int64}, openBF.Vessel}
│    %105 = Base.getindex(ps, 1)::Int64
│    %106 = Core.tuple(%105, t)::Tuple{Int64, Int64}
│    %107 = Base.getindex(%104, %106)::openBF.Vessel
│    %108 = Base.getproperty(n, :vessels)::Dict{Tuple{Int64, Int64}, openBF.Vessel}
│    %109 = Base.getindex(ps, 2)::Int64
│    %110 = Core.tuple(%109, t)::Tuple{Int64, Int64}
│    %111 = Base.getindex(%108, %110)::openBF.Vessel
│    %112 = Base.getproperty(n, :vessels)::Dict{Tuple{Int64, Int64}, openBF.Vessel}
│    %113 = Core.tuple(t, d)::Tuple{Int64, Int64}
│    %114 = Base.getindex(%112, %113)::openBF.Vessel
└───        openBF.solveAnastomosis(%107, %111, %114)
24 ┄        goto #30
25 ─ %117 = (outdeg == 2)::Bool
└───        goto #30 if not %117
26 ─ %119 = Graphs.outneighbors::Core.Const(Graphs.outneighbors)
│    %120 = Base.getproperty(n, :graph)::Graphs.SimpleGraphs.SimpleDiGraph
│    %121 = (%119)(%120, t)::Vector{T} where T<:Integer
│    %122 = Core.apply_type(openBF.Vector, openBF.Int64)::Core.Const(Vector{Int64})
│           (@_18 = %121)
│    %124 = (@_18 isa %122)::Bool
└───        goto #28 if not %124
27 ─        goto #29
28 ─ %127 = Base.convert(%122, @_18)::Vector{Int64}
└───        (@_18 = Core.typeassert(%127, %122))
29 ┄        (ds = @_18::Vector{Int64})
│    %130 = Base.getproperty(n, :vessels)::Dict{Tuple{Int64, Int64}, openBF.Vessel}
│    %131 = Core.tuple(s, t)::Tuple{Int64, Int64}
│    %132 = Base.getindex(%130, %131)::openBF.Vessel
│    %133 = Base.getproperty(n, :vessels)::Dict{Tuple{Int64, Int64}, openBF.Vessel}
│    %134 = t::Int64
│    %135 = Base.getindex(ds, 1)::Int64
│    %136 = Base.getindex(%133, %134, %135)::openBF.Vessel
│    %137 = Base.getproperty(n, :vessels)::Dict{Tuple{Int64, Int64}, openBF.Vessel}
│    %138 = t::Int64
│    %139 = Base.getindex(ds, 2)::Int64
│    %140 = Base.getindex(%137, %138, %139)::openBF.Vessel
└───        openBF.join_vessels!(n, %132, %136, %140)
30 ┄        (@_5 = Base.iterate(%2, %14))
│    %143 = (@_5 === nothing)::Bool
│    %144 = Base.not_int(%143)::Bool
└───        goto #32 if not %144
31 ─        goto #2
32 ┄        (@_19 = nothing)
│    %148 = (@_19 isa %1)::Core.Const(true)
└───        goto #34 if not %148
33 ─        goto #35
34 ─        Core.Const(:(Base.convert(%1, @_19)))
└───        Core.Const(:(@_19 = Core.typeassert(%151, %1)))
35 ┄        return @_19
``````
and since the color coding is lost, here are screenshots

I think that `line 51` refers to this

``````%33  = Base.getproperty(n, :graph)::Graphs.SimpleGraphs.SimpleDiGraph
``````

but I’m not really understanding what the problem could be there.

The same warning occurs every time I use a function from `Graphs.jl`

I think this is due to your `struct` having an abstractly typed field.

You can fix this in many different ways, from specific to generic:

``````struct Network
graph::SimpleDiGraph{Int}
...
end
``````
``````struct Network{V<:Integer}
graph::SimpleDiGraph{V}
...
end
``````
``````struct Network{G<:AbstractGraph}
graph::G
...
end
``````
2 Likes

that did the trick thanks!

``````@_5::Union{Nothing, Tuple{Graphs.SimpleGraphs.SimpleEdge{Int64}, Int64}}
``````

I think it is related to the iterator

``````for edge in n.edges
...
end
``````

An iterator always returns `nothing` at the end, so this type-stability is 1) expected and 2) a small `Union` between two types, so the compiler is not bothered at all. That is why you see it in yellow and not red

1 Like