# Solving type instability when using Graphs.jl

**URL:** <https://discourse.julialang.org/t/solving-type-instability-when-using-graphs-jl/112862>\
**Category:** Graphs\
**Tags:** profiling, code\_warntype, type-stability, graphs\
**Created:** [April 12, 2024, 8:37am UTC](https://discourse.julialang.org/t/solving-type-instability-when-using-graphs-jl/112862 "2024-04-12T08:37:39Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![alemelis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alemelis/32/208433_2.png) [@alemelis](https://discourse.julialang.org/u/alemelis)\
**Post date:** [April 12, 2024, 8:37am UTC](https://discourse.julialang.org/t/solving-type-instability-when-using-graphs-jl/112862/1 "2024-04-12T08:37:39Z")

</div>

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.

 ![Screenshot 2024-04-12 at 09.13.58](https://global.discourse-cdn.com/julialang/original/3X/e/5/e5433432a0c0d971e81c6ed37265689689870108.png)

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**
>
> ```julia
> 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
> 
> ```Julia
> 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

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

```

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

> **\`warntype\_clicked()\` result**
>
> ```julia
> 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**
>
> ![Screenshot 2024-04-12 at 09.30.44](https://global.discourse-cdn.com/julialang/original/3X/0/c/0cc2d39b9f2abc63e6a8860f16cdaa4ca02edb57.png)  
> ![Screenshot 2024-04-12 at 09.31.00](https://global.discourse-cdn.com/julialang/original/3X/f/3/f30124e0b9461f972038bdb9aa264ad09edc6b06.png)  
> ![Screenshot 2024-04-12 at 09.31.10](https://global.discourse-cdn.com/julialang/original/3X/e/c/ecac84bb1c242108af0ffadff3175607b76ca937.png)

I think that `line 51` refers to this

```julia
%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`

---

<div class="post-metadata">

**Author:** ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)\
**Post date:** [April 12, 2024, 9:00am UTC](https://discourse.julialang.org/t/solving-type-instability-when-using-graphs-jl/112862/2 "2024-04-12T09:00:23Z")

</div>

> [@alemelis](#):
>
> ```julia
> struct Network
> graph::SimpleDiGraph
> ...
> end
> 
> ```

I think this is due to your `struct` having an [abstractly typed field](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-abstract-container).

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

```julia
struct Network
    graph::SimpleDiGraph{Int}
    ...
end

```

```julia
struct Network{V<:Integer}
    graph::SimpleDiGraph{V}
    ...
end

```

```julia
struct Network{G<:AbstractGraph}
    graph::G
    ...
end

```

---

<div class="post-metadata">

**Author:** ![alemelis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alemelis/32/208433_2.png) [@alemelis](https://discourse.julialang.org/u/alemelis)\
**Post date:** [April 12, 2024, 9:31am UTC](https://discourse.julialang.org/t/solving-type-instability-when-using-graphs-jl/112862/3 "2024-04-12T09:31:38Z")

</div>

that did the trick thanks!

what about the yellow line?

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

```

I think it is related to the iterator

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

```

---

<div class="post-metadata">

**Author:** ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)\
**Post date:** [April 12, 2024, 10:05am UTC](https://discourse.julialang.org/t/solving-type-instability-when-using-graphs-jl/112862/4 "2024-04-12T10:05:22Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![alemelis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alemelis/32/208433_2.png) [@alemelis](https://discourse.julialang.org/u/alemelis)\
**Post date:** [April 12, 2024, 10:50am UTC](https://discourse.julialang.org/t/solving-type-instability-when-using-graphs-jl/112862/5 "2024-04-12T10:50:56Z")

</div>

thanks for the reply @gdalle
