# Invalidations caused by FixedPointNumbers and GeometryBasics

**URL:** <https://discourse.julialang.org/t/invalidations-caused-by-fixedpointnumbers-and-geometrybasics/139232>\
**Category:** Internals & Design\
**Tags:** question, package, geometrybasics, mtk, fixedpointnumbers\
**Created:** [September 6, 2026, 12:34pm UTC](https://discourse.julialang.org/t/invalidations-caused-by-fixedpointnumbers-and-geometrybasics/139232 "2026-09-06T12:34:30Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [September 6, 2026, 12:34pm UTC](https://discourse.julialang.org/t/invalidations-caused-by-fixedpointnumbers-and-geometrybasics/139232/1 "2026-09-06T12:34:30Z")

</div>

The FixedPointNumbers package is causing many invalidations, even though it does not engage in type piracy. For details, see: [Loading FixedPointNumbers invalidates ~7,700 precompiled method instances (7x slower first mtkcompile) · Issue #331 · JuliaMath/FixedPointNumbers.jl · GitHub](https://github.com/JuliaMath/FixedPointNumbers.jl/issues/331)

My question: What is causing these invalidations? Should that be fixed in Julia itself, or in FixedPointNumbers, or is there no way to fix it?

I encounter this problem mainly when using GLMakie, which depends on FixedPointNumbers.

**UPDATE:** GeometryBasics has a similar problem: [Loading GeometryBasics invalidates ~11,000 precompiled method instances (7x slower first mtkcompile) · Issue #284 · JuliaGeometry/GeometryBasics.jl · GitHub](https://github.com/JuliaGeometry/GeometryBasics.jl/issues/284). Both issues must be fixed before loading GLMakie does not create long delays for MTK any more.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [September 6, 2026, 10:21pm UTC](https://discourse.julialang.org/t/invalidations-caused-by-fixedpointnumbers-and-geometrybasics/139232/2 "2026-09-06T22:21:48Z")

</div>

Might be case 4 mentioned in [Type piracy · Aqua.jl](https://juliatesting.github.io/Aqua.jl/stable/piracies/), not explained like the others though. I know of invalidations where additional methods violate a prior assumption of a function only having a few (defaulting to 3 according to `?Base.Experimental.@max_methods`) methods when abstract types are inferred for inputs, but a cursory glance at `methods(-, Tuple{Real})` shows 9 in Base, so I doubt that assumption was made.

Probably unimportant, but I don’t see how the `Union{}` type described in case 3 and mentioned in the issue is practically relevant when nobody can instantiate it and won’t instantiate empty `Union{}`-parametric types, that is these call signatures aren’t worth compiling in the first place.

---

<div class="post-metadata">

**Author:** ![kimikage](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kimikage/32/14534_2.png) [@kimikage](https://discourse.julialang.org/u/kimikage)\
**Post date:** [September 27, 2026, 4:20am UTC](https://discourse.julialang.org/t/invalidations-caused-by-fixedpointnumbers-and-geometrybasics/139232/3 "2026-09-27T04:20:21Z")

</div>

Julia v1.13 (v1.13.1) has been released. Updating the information on how the situation has changed might be helpful to developers.

As mentioned in [Loading FixedPointNumbers invalidates ~7,700 precompiled method instances (7x slower first mtkcompile) · Issue #331 · JuliaMath/FixedPointNumbers.jl · GitHub](https://github.com/JuliaMath/FixedPointNumbers.jl/issues/331#issuecomment-5656957156), I suspect that `SymbolicUtils.jl` is partly to blame.  
Of course, `SymbolicUtils.jl` might literally be the “victim”.

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [September 27, 2026, 8:12am UTC](https://discourse.julialang.org/t/invalidations-caused-by-fixedpointnumbers-and-geometrybasics/139232/4 "2026-09-27T08:12:00Z")

</div>

No improvement using Julia 1.13.1:

```julia-auto
ufechner@framework:~/tmp$ julia --startup-file=no --project=. mwe.jl
mtkcompile: 0.04 s
ufechner@framework:~/tmp$ julia --startup-file=no --project=. mwe.jl
mtkcompile: 0.04 s
ufechner@framework:~/tmp$ LOAD_FPN=1 julia --startup-file=no --project=. mwe.jl
mtkcompile: 28.24 s
ufechner@framework:~/tmp$ LOAD_FPN=1 julia --startup-file=no --project=. mwe.jl
mtkcompile: 28.2 s

```

---

<div class="post-metadata">

**Author:** ![kimikage](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kimikage/32/14534_2.png) [@kimikage](https://discourse.julialang.org/u/kimikage)\
**Post date:** [September 28, 2026, 10:39am UTC](https://discourse.julialang.org/t/invalidations-caused-by-fixedpointnumbers-and-geometrybasics/139232/5 "2026-09-28T10:39:36Z")

</div>

Thank you for updating the information.  
Regarding the `checked_mul` issue in FixedPointNumbers v0.9, there are limits to what can be addressed within the package itself.  
I have filed two issues regarding workarounds.

> <https://github.com/JuliaLang/julia/issues/63452>
>
> Currently, checked arithmetic functions such as \`checked\_add\` and \`checked\_mul\` …do not have fallback implementations.
> For this reason, if type inference fails, the method cannot be resolved.
> \`\`\`julia
> julia\> which(Base.Checked.checked\_mul, Tuple{Integer, Any})
> ERROR: Calling invoke(f, t, args...) would throw:
> MethodError: checked\_mul(::Integer, ::Any) is ambiguous.
> 
> Candidates:
> checked\_mul(a::BigInt, b::BigInt)
> @ Base.GMP gmp.jl:864
> checked\_mul(x::T, y::T) where T\<:Integer
> @ Base.Checked checked.jl:292
> checked\_mul(x::Integer, y::Integer)
> @ Base.Checked checked.jl:28
> To resolve the ambiguity, try making one of the methods more specific, or adding a new method more specific than any of the existing applicable methods.
> \`\`\`
> 
> This carries the risk of triggering invalidation when adding new methods for new types.
> In fact, FixedPointNumbers.jl began using \`checked\_mul\` etc. in v0.9, and (although the situation is somewhat unusual), ModelingToolkit.jl (or its dependencies) is encountering a critical invalidation issue. (cf. https://github.com/JuliaMath/FixedPointNumbers.jl/issues/331)
> 
> A demo is shown below.
> \`\`\`julia
> julia\> using SnoopCompileCore
> 
> julia\> import Base.Checked: checked\_mul
> 
> julia\> checked\_mul(x, y) = checked\_mul(convert(Integer, x), convert(Integer, y)); # fallback
> 
> julia\> using ModelingToolkit # v11.45.1
> 
> julia\> invs = @snoop\_invalidations Base.Checked.checked\_mul(::Real, T::Type) = T(); # invalidation trigger
> 
> julia\> using SnoopCompile
> 
> julia\> length(uinvalidated(invs))
> 4206 # without fallback
> 0 # with fallback
> 
> Julia Version 1.13.1
> Commit 96ca370cf0 (2026-09-25 19:34 UTC)
> Build Info:
> Official https://julialang.org release
> Platform Info:
> OS: Windows (x86\_64-w64-mingw32)
> CPU: 8 × 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz
> WORD\_SIZE: 64
> LLVM: libLLVM-20.1.8 (ORCJIT, tigerlake)
> GC: Built with stock GC
> Threads: 1 default, 1 interactive, 1 GC (on 8 virtual cores)
> \`\`\`
> 
> Of course, I believe the issue of type instability should be addressed on the package side.
> In addition, end users can mitigate the problem by changing the order in which packages are loaded.
> 
> There is another, independent workaround for this issue (xref: #63455).

> <https://github.com/JuliaLang/julia/issues/63455>
>
> For background information, please refer to issue #63452.
> 
> One of the (potential…) direct backedges of \`checked\_mul(::Integer, ::Any)\` is \`//(::Rational, ::Integer)\` (and its variants).
> 
> \`\`\`julia
> julia\> code\_warntype(//, (Rational, Integer))
> MethodInstance for //(::Rational, ::Integer)
> from //(x::Rational, y::Integer) @ Base rational.jl:93
> Arguments
> #self#::Core.Const(//)
> x::Rational
> y::Integer
> Locals
> @\_4::Int64
> yn::Any
> xn::Any
> Body::Rational
> 1 ─ %1 = Base.divgcd::Core.Const(Base.divgcd)
> │ %2 = Base.promote::Core.Const(promote)
> │ %3 = Base.getproperty(x, :num)::Integer
> │ %4 = (%2)(%3, y)::Tuple{Any, Any}
> │ %5 = Core.\_apply\_iterate(Base.iterate, %1, %4)::Tuple{Any, Any}
> │ %6 = Base.indexed\_iterate(%5, 1)::Core.PartialStruct(Tuple{Any, Int64}, Union{Nothing, Bool}\[0, 0\], Any\[Any, Core.Const(2)\])
> │ (xn = Core.getfield(%6, 1))
> │ (@\_4 = Core.getfield(%6, 2))
> │ %9 = @\_4::Core.Const(2)
> │ %10 = Base.indexed\_iterate(%5, 2, %9)::Core.PartialStruct(Tuple{Any, Int64}, Union{Nothing, Bool}\[0, 0\], Any\[Any, Core.Const(3)\])
> │ (yn = Core.getfield(%10, 1))
> │ %12 = Base.checked\_den::Core.Const(Base.checked\_den)
> │ %13 = xn::Any
> │ %14 = Base.checked\_mul::Core.Const(Base.Checked.checked\_mul)
> │ %15 = Base.getproperty(x, :den)::Integer
> │ %16 = yn::Any
> │ %17 = (%14)(%15, %16)::Any
> │ %18 = (%12)(%13, %17)::Rational
> └── return %18
> \`\`\`
> The \`Base.divgcd\` is defined with only one method that takes two \`Integer\` arguments, and it is expected to return two \`Integer\`s. However, it actually may returns \`(Any, Any)\` for abstract type arguments, as showed above.
> 
> https://github.com/JuliaLang/julia/blob/e4624f55020e91cdd43465f6f6e87430f45bd535/base/rational.jl#L52-L62
> 
> I think it’s a reasonable choice to give up on type inference early on for methods that aren’t worth specializing.
> On the other hand, (though this may be an edge case), since the method is actually precompiled, I think it’s acceptable to have some degree of type assertions.
> To begin with, is \`::Tuple{TX, TY}\` generally correct? I don't think there's any guarantee that the results will be in \`TX\` and \`TY\`. (cf. PR #51944)
> 
> The \`Base.divgcd\` is a internal function and AFAK only used in rational.jl.
> 
> \`\`\`julia
> julia\> versioninfo()
> Julia Version 1.13.1
> Commit 96ca370cf0 (2026-09-25 19:34 UTC)
> Build Info:
> Official https://julialang.org release
> Platform Info:
> OS: Windows (x86\_64-w64-mingw32)
> CPU: 8 × 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz
> WORD\_SIZE: 64
> LLVM: libLLVM-20.1.8 (ORCJIT, tigerlake)
> GC: Built with stock GC
> Threads: 1 default, 1 interactive, 1 GC (on 8 virtual cores)
> \`\`\`

---

<div class="post-metadata">

**Author:** ![kimikage](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kimikage/32/14534_2.png) [@kimikage](https://discourse.julialang.org/u/kimikage)\
**Post date:** [October 10, 2026, 12:25am UTC](https://discourse.julialang.org/t/invalidations-caused-by-fixedpointnumbers-and-geometrybasics/139232/6 "2026-10-10T00:25:32Z")

</div>

@ufechner7

SymbolicUtils.jl v4.50.0 has been released, incorporating:

> <https://github.com/JuliaSymbolics/SymbolicUtils.jl/pull/1127>
>
> Follow-up to the type-stability note in https://github.com/JuliaSymbolics/Symbol…icUtils.jl/issues/1101#issuecomment-5884776264.
> 
> The \`sqrt\`/\`cbrt\` branches of \`^\` computed \`b::Rational // 2\` (and \`// 3\`). In the \`^(::BasicSymbolic, ::Number)\` specialization, that makes inference go through \`//(::Rational, ::Int64)\` with an abstract \`Rational\`. That call has a backedge to \`checked\_mul(::Integer, ::Any)\` (JuliaLang/julia#63455), and FixedPointNumbers 0.9 adds a \`checked\_mul\` method, so loading it invalidated \`^\` and everything inferred through it. \`b // 2\` behaves the same at runtime, since it dispatches on the concrete \`Rational\`, but inference no longer specializes \`//\` on the abstract type: SymbolicUtils compiles with \`max\_methods=1\`, and \`(Number, Int)\` matches three \`//\` methods, so the call is left dynamic.
> 
> \*\*Measured\*\* with SnoopCompileCore and FixedPointNumbers 0.9.1 against current master (v4.49.0): distinct SymbolicUtils method instances invalidated by \`using FixedPointNumbers\` after a small \`^\` workload.
> 
> | Julia | master | this PR |
> |---|---|---|
> | 1.12.7 | 83 (70 of them in the \`checked\_mul\` tree, all entering at \`^(::BasicSymbolic, ::Number)\` → \`//(::Rational, ::Int64)\`) | 13 |
> | 1.13.1 | 1163 | 1163 |
> 
> On 1.13.1 the \`checked\_mul\` tree is gone too, but a separate tree rooted at FixedPointNumbers' \`length(::StepRange{X, X})\` invalidates 1159 method instances through \`Dict{…, StepRange}\` operations, \`^\` included. This PR doesn't address that one.
> 
> \*\*Tests:\*\* none added, because behaviour is unchanged (the \`sqrt\`/\`cbrt\` folds give identical results before and after). There's no automated invalidation test either, since the counts depend on the Julia version and on what has been compiled. On the merged branch I ran \`basics\`, \`methods\` and \`rulesets\` on Julia 1.10.12 and 1.12.7; before the merge, \`polyform\` (both versions) and \`fuzz\` (1.12.7) passed too. I did not run the full suite.
> 
> 🤖 Generated with \[Claude Code\](https://claude.com/claude-code)

How does it work when combined with FixedPointNumbers.jl v0.9.2?

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [October 11, 2026, 8:38pm UTC](https://discourse.julialang.org/t/invalidations-caused-by-fixedpointnumbers-and-geometrybasics/139232/7 "2026-10-11T20:38:04Z")

</div>

Working much better now! I updated the issue for FixedPointNumbers.  
Now we only need some progress with the invalidations caused by [GeometryBasics](https://github.com/JuliaGeometry/GeometryBasics.jl/issues/284).
