Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5bbfb25
Widen traced-type signatures to accept covariant element types
oschulz Jul 8, 2026
25f12ff
Make `similar` of a concrete array with a traced eltype return a trac…
oschulz Jul 8, 2026
74e69bb
Split Reactant number types by numeric kind
oschulz Jul 7, 2026
238936f
Adapt traced number operations to the split number types
oschulz Jul 7, 2026
a31035e
Adapt type tracing to the split number types
oschulz Jul 7, 2026
337242e
Adapt traced arrays, ops, ranges, and rationals to the split number t…
oschulz Jul 7, 2026
cd98dd3
Adapt concrete number ops, codegen, and Enzyme integration
oschulz Jul 7, 2026
8e15817
Adapt stdlib overloads and package extensions to the split number types
oschulz Jul 7, 2026
c9ecf7b
Resolve method ambiguities against Base's specialized number methods
oschulz Jul 7, 2026
db04e94
Fix test-suite fallout from the number-kind split
oschulz Jul 7, 2026
f31642a
Fix traced-integer exponents, Active scalar autodiff, and type concre…
oschulz Jul 7, 2026
bd1ef8e
Add number-kind hierarchy tests and format
oschulz Jul 7, 2026
1c65ac4
Split CuTracedRNumber by numeric kind in the CUDA extension
oschulz Jul 8, 2026
139bb91
Fix integration fallout and enforce the TracedRArray element invariant
oschulz Jul 8, 2026
a6ff657
Adopt Enzyme's upstream wrapped-number activity classification
oschulz Jul 8, 2026
4c17102
Resolve method ambiguities and traced-rational promotion gaps
oschulz Jul 8, 2026
78cb028
Document the number kind hierarchy
oschulz Jul 8, 2026
243e5ad
Format
oschulz Jul 8, 2026
0210c81
Drop formatter churn unrelated to the number-type split
oschulz Jul 8, 2026
362cf8f
Exclude `Union{}` element types from `AnyTracedRArray`
oschulz Jul 9, 2026
e6b56d7
Expect kind-typed traced arrays in the tracing tests
oschulz Jul 9, 2026
07a74a8
Keep the `apply_type_with_promotion` docstring attached
oschulz Jul 9, 2026
57c1a90
Disambiguate `UnitRange` `getindex` against 1.10's overflow-safe grou…
oschulz Jul 9, 2026
9b59c51
Require Enzyme 0.13.177
oschulz Jul 9, 2026
39f481d
Resolve getindex ambiguities against Base, LinearAlgebra, and SparseA…
oschulz Jul 9, 2026
385e395
Disambiguate Bool rounding of traced floats
oschulz Jul 9, 2026
80f197e
Widen `Missing`/`Nothing` promotion to the traced number kinds
oschulz Jul 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ DLFP8Types = "0.1"
Dates = "1.10"
Downloads = "1.6"
EnumX = "1"
Enzyme = "0.13.115"
Enzyme = "0.13.177"
EnzymeCore = "0.8.14"
FFTW = "1"
FileWatching = "1.10"
Expand Down
8 changes: 8 additions & 0 deletions docs/src/api/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ Reactant.to_rarray
```@docs
ConcreteRArray
ConcreteRNumber
Reactant.RNumber
Reactant.RReal
RInteger
RFloat
RComplex
Reactant.traced_number_type
Reactant.pjrt_number_type
Reactant.ifrt_number_type
```

## Inspect Generated HLO
Expand Down
16 changes: 15 additions & 1 deletion ext/ReactantAbstractFFTsExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,21 @@ using Reactant.Ops: @opcall

# FFTW.jl defines methods on StridedArrays, and we have to be more specific than FFTW to
# catch its methods.
const AnyStridedTracedRArray{T,N} = StridedArray{TracedRNumber{T},N}
const AnyStridedTracedRArray{T,N} = StridedArray{<:TracedRNumber{T},N}

# Traced reals hit the `fftfloat`-based conversion path in AbstractFFTs, whose
# fallback only supports the BLAS float types and copies through a host array.
function AbstractFFTs._fftfloat(::Type{T}) where {T<:TracedRNumber}
return Reactant.traced_number_type(AbstractFFTs._fftfloat(Reactant.unwrapped_eltype(T)))
end

function AbstractFFTs.complexfloat(x::AbstractArray{<:Reactant.TracedRReal{T}}) where {T}
return Reactant.promote_to(Reactant.TracedRArray{complex(float(T)),ndims(x)}, x)
end

function AbstractFFTs.realfloat(x::AbstractArray{<:Reactant.TracedRReal{T}}) where {T}
return Reactant.promote_to(Reactant.TracedRArray{float(T),ndims(x)}, x)
end

# To automatically convert FFT plans to traced versions
# To extend a user needs to extend Reactant.reactant_fftplan for their plan type
Expand Down
132 changes: 95 additions & 37 deletions ext/ReactantCUDAExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,27 +63,53 @@ end

Reactant.use_overlayed_version(::CuTracedArray) = true

struct CuTracedRNumber{T,A} <: Number
ptr::Core.LLVMPtr{T,A}
# Split by numeric kind, mirroring the traced host types, so that kernel-side
# numbers subtype Integer and AbstractFloat where possible.
for (CuT, AbstractT) in (
(:CuTracedRInteger, :(Reactant.RInteger)),
(:CuTracedRFloat, :(Reactant.RFloat)),
(:CuTracedRComplex, :(Reactant.RComplex)),
)
@eval begin
struct $CuT{T,A} <: $AbstractT{T}
ptr::Core.LLVMPtr{T,A}

function CuTracedRNumber{T,A}(xs::TracedRNumber) where {T,A}
Reactant.Compiler.guard_from_gc_for_module(MLIR.IR.current_module(), xs)
ptr = Base.reinterpret(Core.LLVMPtr{T,CUDA.AS.Global}, Base.pointer_from_objref(xs))
return new(ptr)
end
function CuTracedRNumber{T,A}(ptr::Core.LLVMPtr{T,A}) where {T,A}
return new(ptr)
function $CuT{T,A}(xs::TracedRNumber) where {T,A}
Reactant.Compiler.guard_from_gc_for_module(MLIR.IR.current_module(), xs)
ptr = Base.reinterpret(
Core.LLVMPtr{T,CUDA.AS.Global}, Base.pointer_from_objref(xs)
)
return new(ptr)
end
function $CuT{T,A}(ptr::Core.LLVMPtr{T,A}) where {T,A}
return new(ptr)
end
end

Reactant.use_overlayed_version(::$CuT) = true
end
end

Reactant.use_overlayed_version(::CuTracedRNumber) = true
const CuTracedRReal{T,A} = Union{CuTracedRInteger{T,A},CuTracedRFloat{T,A}}
const CuTracedRNumber{T,A} = Union{CuTracedRReal{T,A},CuTracedRComplex{T,A}}

@inline cu_traced_number_type(::Type{T}) where {T<:Integer} = CuTracedRInteger{T}
@inline cu_traced_number_type(::Type{T}) where {T<:AbstractFloat} = CuTracedRFloat{T}
@inline cu_traced_number_type(::Type{T}) where {T<:Complex} = CuTracedRComplex{T}

@inline function CuTracedRNumber{T,A}(xs::TracedRNumber) where {T,A}
return cu_traced_number_type(T){A}(xs)
end
@inline function CuTracedRNumber{T,A}(ptr::Core.LLVMPtr{T,A}) where {T,A}
return cu_traced_number_type(T){A}(ptr)
end

Base.@nospecializeinfer Reactant.is_traced_number(
@nospecialize(T::Type{<:CuTracedRNumber})
) = true
Reactant.unwrapped_eltype(::Type{<:CuTracedRNumber{T}}) where {T} = T

@inline CuTracedRNumber{T,A}(val::Number) where {T,A} = convert(CuTracedRNumber{T,A}, val)
@inline (::Type{CuT})(val::Number) where {CuT<:CuTracedRNumber} = convert(CuT, val)

function Base.getindex(RN::CuTracedRNumber{T,A}) where {T,A}
align = alignment(RN)
Expand All @@ -92,6 +118,10 @@ end

Base.convert(::Type{T}, RN::CuTracedRNumber) where {T<:Number} = convert(T, getindex(RN))

# Defined per numeric kind: a single method on the `CuTracedRNumber` union
# would be ambiguous with Base's methods on `Real`/`Integer`/`AbstractFloat`.
const CU_TRACED_NUMBER_KINDS = (CuTracedRInteger, CuTracedRFloat, CuTracedRComplex)

for jlop in (
:(Base.min),
:(Base.mod),
Expand All @@ -106,10 +136,32 @@ for jlop in (
:(Base.:(==)),
:(Base.:(!=)),
)
@eval begin
@inline $jlop(a::CuTracedRNumber, b::CuTracedRNumber) = $jlop(a[], b[])
@inline $jlop(a::CuTracedRNumber{T,A}, b::Number) where {T,A} = $jlop(a[], b)
@inline $jlop(a::Number, b::CuTracedRNumber{T,A}) where {T,A} = $jlop(a, b[])
for K1 in CU_TRACED_NUMBER_KINDS
@eval begin
@inline $jlop(a::$K1, b::Number) = $jlop(a[], b)
@inline $jlop(a::Number, b::$K1) = $jlop(a, b[])
end
for K2 in CU_TRACED_NUMBER_KINDS
@eval @inline $jlop(a::$K1, b::$K2) = $jlop(a[], b[])
end
for X in (
:Real,
:Integer,
:Bool,
:AbstractFloat,
:Complex,
:(Complex{Bool}),
:Rational,
:BigInt,
:BigFloat,
:AbstractIrrational,
)
jlop == :(Base.:^) && X === :Integer && continue
@eval begin
@inline $jlop(a::$X, b::$K1) = $jlop(a, b[])
@inline $jlop(a::$K1, b::$X) = $jlop(a[], b)
end
end
end
end

Expand All @@ -123,28 +175,28 @@ end
@inline Base.ifelse(cond::CuTracedRNumber, a::CuTracedRNumber, b::CuTracedRNumber) =
ifelse(cond[], a[], b[])

Base.@constprop :aggressive @inline Base.:^(
a::CuTracedRNumber{T,A}, b::Integer
) where {T,A} = ^(a[], b)
for K in CU_TRACED_NUMBER_KINDS
@eval Base.@constprop :aggressive @inline Base.:^(a::$K, b::Integer) = ^(a[], b)
end

@inline Base.unsafe_trunc(::Type{T}, a::CuTracedRNumber) where {T} =
Base.unsafe_trunc(T, a[])

for jlop in (:(Base.:+), :(Base.:-), :(Base.isnan), :(Base.isfinite), :(Base.isinf))
@eval begin
@inline $jlop(a::CuTracedRNumber) = $jlop(a[])
end
for jlop in (:(Base.:+), :(Base.:-), :(Base.isnan), :(Base.isfinite), :(Base.isinf)),
K in CU_TRACED_NUMBER_KINDS

@eval @inline $jlop(a::$K) = $jlop(a[])
end

Base.OneTo(x::CuTracedRNumber{<:Integer}) = Base.OneTo(x[])
Base.OneTo(x::CuTracedRInteger) = Base.OneTo(x[])

@static if isdefined(Base, :unchecked_oneto)
function Base.unchecked_oneto(x::CuTracedRNumber{<:Integer})
return Base.unchecked_oneto(x[])
end
end

@inline function Base.convert(CT::Type{CuTracedRNumber{Float64,1}}, x::Number)
@inline function Base.convert(CT::Type{<:CuTracedRNumber{Float64,1}}, x::Number)
return CT(
Base.reinterpret(
Core.LLVMPtr{Float64,1},
Expand All @@ -168,7 +220,7 @@ end
)
end

@inline function Base.convert(CT::Type{CuTracedRNumber{Float32,1}}, x::Number)
@inline function Base.convert(CT::Type{<:CuTracedRNumber{Float32,1}}, x::Number)
return CT(
Base.reinterpret(
Core.LLVMPtr{Float32,1},
Expand Down Expand Up @@ -273,8 +325,12 @@ function Base.show(io::IO, a::AT) where {AT<:CuTracedArray}
Printf.@printf(io, "%s cu traced array at %p", join(size(a), '×'), Int(pointer(a)))
end

function Base.show(io::IO, a::AT) where {AT<:CuTracedRNumber}
Printf.@printf(io, "%s cu traced rnumber at %p", join(size(a), '×'), Int(pointer(a)))
for K in CU_TRACED_NUMBER_KINDS
@eval function Base.show(io::IO, a::AT) where {AT<:$K}
Printf.@printf(
io, "%s cu traced rnumber at %p", join(size(a), '×'), Int(pointer(a))
)
end
end

## array interface
Expand Down Expand Up @@ -811,7 +867,7 @@ function compile(job)
MLIR.IR.setattr!(MLIR.IR.Operation(cur_module), dl_attr_name, MLIR.IR.Attribute(dl))
end

String(Reactant.TracedUtils.get_attribute_by_name(linkRes, "sym_name"))
return String(Reactant.TracedUtils.get_attribute_by_name(linkRes, "sym_name"))
end

return LLVMFunc{job.source.specTypes.parameters[1],job.source.specTypes}(nothing, entry)
Expand Down Expand Up @@ -1595,15 +1651,17 @@ Base.@nospecializeinfer function Reactant.traced_type_inner(
return A
end

Base.@nospecializeinfer function Reactant.traced_type_inner(
@nospecialize(A::Type{<:CuTracedRNumber}),
seen,
@nospecialize(mode::Reactant.TraceMode),
@nospecialize(track_numbers::Type),
@nospecialize(ndevices),
@nospecialize(runtime)
)
return A
for CuT in (:CuTracedRNumber, :CuTracedRInteger, :CuTracedRFloat, :CuTracedRComplex)
@eval Base.@nospecializeinfer function Reactant.traced_type_inner(
@nospecialize(A::Type{<:$CuT}),
seen,
@nospecialize(mode::Reactant.TraceMode),
@nospecialize(track_numbers::Type),
@nospecialize(ndevices),
@nospecialize(runtime)
)
return A
end
end

Base.@nospecializeinfer function Reactant.traced_type_inner(
Expand Down
16 changes: 10 additions & 6 deletions ext/ReactantOneHotArraysExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ using ReactantCore: ReactantCore
using Reactant.Ops: @opcall

__compatible_eltype(::Type{T}, ::Type{U}) where {T,U} = T
function __compatible_eltype(::Type{TracedRNumber{T}}, ::Type{TracedRNumber{U}}) where {T,U}
return TracedRNumber{T}
function __compatible_eltype(
::Type{<:TracedRNumber{T}}, ::Type{<:TracedRNumber{U}}
) where {T,U}
return Reactant.traced_number_type(T)
end
__compatible_eltype(::Type{<:TracedRNumber{T}}, ::Type{U}) where {T,U} = T
function __compatible_eltype(::Type{T}, ::Type{<:TracedRNumber{U}}) where {T,U}
return Reactant.traced_number_type(T)
end
__compatible_eltype(::Type{TracedRNumber{T}}, ::Type{U}) where {T,U} = T
__compatible_eltype(::Type{T}, ::Type{TracedRNumber{U}}) where {T,U} = TracedRNumber{T}

function Reactant.traced_type_inner(
@nospecialize(_::Type{OneHotArray{T,N,Np1,I}}),
Expand Down Expand Up @@ -60,7 +64,7 @@ function OneHotArrays.onehotbatch(data::AnyTracedRArray{<:Any,N}, labels) where
)
data = ReactantCore.materialize_traced_array(reshape(data, 1, size(data)...))
indices = UInt32.(@opcall(findfirst(data .== labels_expanded; dimension=1)))
return OneHotArray{TracedRNumber{UInt32},N,N + 1,typeof(indices)}(
return OneHotArray{Reactant.TracedRInteger{UInt32},N,N + 1,typeof(indices)}(
indices, length(labels)
)
end
Expand All @@ -73,7 +77,7 @@ function OneHotArrays.onehotbatch(
TracedRNumber{UInt32} ∘ Base.Fix2(+, 1 - first(labels)),
ReactantCore.materialize_traced_array(data),
)
return OneHotArray{TracedRNumber{UInt32},N,N + 1,typeof(indices)}(
return OneHotArray{Reactant.TracedRInteger{UInt32},N,N + 1,typeof(indices)}(
indices, length(labels)
)
end
Expand Down
6 changes: 3 additions & 3 deletions ext/ReactantSparseArraysExt/Errors.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# We don't yet support sparse arrays. Throwing errors on this dispatches to avoid
# ambiguities.
function Base.getindex(::AbstractSparseArray{TracedRNumber{T},N,1}, ::Int64) where {T,N}
function Base.getindex(::AbstractSparseArray{<:TracedRNumber{T},N,1}, ::Int64) where {T,N}
return error("Sparse arrays are not supported by reactant yet.")
end

function Base.getindex(
::AbstractSparseMatrixCSC{TracedRNumber{T}}, ::Int64, ::Int64
::AbstractSparseMatrixCSC{<:TracedRNumber{T}}, ::Int64, ::Int64
) where {T}
return error("Sparse arrays are not supported by reactant yet.")
end

function Base.getindex(::CHOLMOD.Sparse{TracedRNumber{T}}, ::Int64, ::Int64) where {T}
function Base.getindex(::CHOLMOD.Sparse{<:TracedRNumber{T}}, ::Int64, ::Int64) where {T}
return error("Sparse arrays are not supported by reactant yet.")
end
33 changes: 31 additions & 2 deletions ext/ReactantSparseArraysExt/ReactantSparseArraysExt.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
module ReactantSparseArraysExt

using Reactant: Reactant, TracedRNumber
using Reactant: Reactant, TracedRInteger, TracedRNumber
using SparseArrays:
SparseArrays, ReadOnly, AbstractSparseArray, CHOLMOD, AbstractSparseMatrixCSC
SparseArrays,
ReadOnly,
AbstractSparseArray,
AbstractSparseVector,
CHOLMOD,
AbstractSparseMatrixCSC

include("Errors.jl")
include("ReadOnly.jl")

# Scalar indexing disambiguators against the generic traced-array `getindex`;
# they delegate to the SparseArrays implementations.
function Base.getindex(
x::AbstractSparseMatrixCSC{<:TracedRNumber},
i::Union{Int,TracedRInteger{Int}},
j::Union{Int,TracedRInteger{Int}},
)
return Base.invoke(getindex, Tuple{AbstractSparseMatrixCSC,Integer,Integer}, x, i, j)
end

function Base.getindex(
x::AbstractSparseVector{<:TracedRNumber}, i::Union{Int,TracedRInteger{Int}}
)
return Base.invoke(getindex, Tuple{AbstractSparseVector,Integer}, x, i)
end

function Base.getindex(
x::CHOLMOD.Sparse{<:TracedRNumber},
i::Union{Int,TracedRInteger{Int}},
j::Union{Int,TracedRInteger{Int}},
)
return Base.invoke(getindex, Tuple{CHOLMOD.Sparse,Integer,Integer}, x, i, j)
end

end
Loading
Loading