Hello, I want to apply this very simple example of mapreduce involving eachindex on two tuples
function test_standard(x::Tuple, y::Tuple)
length(x) == length(y) || throw(ArgumentError("Input tuples must have the same length."))
mapreduce((a, b) -> (a[1] + b[1], (a[2]..., b[2]...)), eachindex(x)) do i
(x[i]^2 + y[i]^2, (x[i]^3 + y[i]^3,))
end
end
x = (0.2, 0.4, 0.5)
y = (0.1, 0.3, 0.4)
test_standard(x, y) # (0.71, (0.009000000000000003, 0.09100000000000001, 0.189))
But it is type unstable
MethodInstance for test_standard(::Tuple{Float64, Float64, Float64}, ::Tuple{Float64, Float64, Float64})
from test_standard(x::Tuple, y::Tuple) @ Main ~/.julia/dev/QuantumToolbox/misc/script.jl:39
Arguments
#self#::Core.Const(Main.test_standard)
x::Tuple{Float64, Float64, Float64}
y::Tuple{Float64, Float64, Float64}
Locals
#16::var"#test_standard##2#test_standard##3"
#15::var"#test_standard##0#test_standard##1"{Tuple{Float64, Float64, Float64}, Tuple{Float64, Float64, Float64}}
Body::Tuple{Float64, Any}
1 β Core.NewvarNode(:(#16))
β Core.NewvarNode(:(#15))
β %3 = Main.:(==)::Core.Const(==)
β %4 = Main.length::Core.Const(length)
β %5 = (%4)(x)::Core.Const(3)
β %6 = Main.length::Core.Const(length)
β %7 = (%6)(y)::Core.Const(3)
β %8 = (%3)(%5, %7)::Core.Const(true)
βββ goto #3 if not %8
2 β goto #4
3 β Core.Const(:(Main.throw))
β Core.Const(:(Main.ArgumentError))
β Core.Const(:((%12)("Input tuples must have the same length.")))
βββ Core.Const(:((%11)(%13)))
4 β %15 = Main.mapreduce::Core.Const(mapreduce)
β %16 = Main.:(var"#test_standard##0#test_standard##1")::Core.Const(var"#test_standard##0#test_standard##1")
β %17 = Core._typeof_captured_variable(x)::Core.Const(Tuple{Float64, Float64, Float64})
β %18 = Core._typeof_captured_variable(y)::Core.Const(Tuple{Float64, Float64, Float64})
β %19 = Core.apply_type(%16, %17, %18)::Core.Const(var"#test_standard##0#test_standard##1"{Tuple{Float64, Float64, Float64}, Tuple{Float64, Float64, Float64}})
β (#15 = %new(%19, x, y))
β %21 = #15::var"#test_standard##0#test_standard##1"{Tuple{Float64, Float64, Float64}, Tuple{Float64, Float64, Float64}}
β %22 = Main.:(var"#test_standard##2#test_standard##3")::Core.Const(var"#test_standard##2#test_standard##3")
β (#16 = %new(%22))
β %24 = #16::Core.Const(var"#test_standard##2#test_standard##3"())
β %25 = Main.eachindex::Core.Const(eachindex)
β %26 = (%25)(x)::Core.Const(Base.OneTo(3))
β %27 = (%15)(%21, %24, %26)::Tuple{Float64, Any}
βββ return %27
I think the main problem is eachindex, which is not telling the compiler that the length is actually known at compile time.
I found a workaround with something like
tuple_mapreduce(f, op, iters::Tuple...) = reduce(op, map(f, iters...))
tuple_eachindex(x::Tuple) = ntuple(identity, Val(length(x)))
function test_custom(x::Tuple, y::Tuple)
length(x) == length(y) || throw(ArgumentError("Input tuples must have the same length."))
tuple_mapreduce((a, b) -> (a[1] + b[1], (a[2]..., b[2]...)), tuple_eachindex(x)) do i
(x[i]^2 + y[i]^2, (x[i]^3 + y[i]^3,))
end
end
test_custom(x, y) # (0.71, (0.009000000000000003, 0.09100000000000001, 0.189))
And it is type stable
MethodInstance for test_custom(::Tuple{Float64, Float64, Float64}, ::Tuple{Float64, Float64, Float64})
from test_custom(x::Tuple, y::Tuple) @ Main ~/.julia/dev/QuantumToolbox/misc/script.jl:46
Arguments
#self#::Core.Const(Main.test_custom)
x::Tuple{Float64, Float64, Float64}
y::Tuple{Float64, Float64, Float64}
Locals
#18::var"#test_custom##2#test_custom##3"
#17::var"#test_custom##0#test_custom##1"{Tuple{Float64, Float64, Float64}, Tuple{Float64, Float64, Float64}}
Body::Tuple{Float64, Tuple{Float64, Float64, Float64}}
1 β Core.NewvarNode(:(#18))
β Core.NewvarNode(:(#17))
β %3 = Main.:(==)::Core.Const(==)
β %4 = Main.length::Core.Const(length)
β %5 = (%4)(x)::Core.Const(3)
β %6 = Main.length::Core.Const(length)
β %7 = (%6)(y)::Core.Const(3)
β %8 = (%3)(%5, %7)::Core.Const(true)
βββ goto #3 if not %8
2 β goto #4
3 β Core.Const(:(Main.throw))
β Core.Const(:(Main.ArgumentError))
β Core.Const(:((%12)("Input tuples must have the same length.")))
βββ Core.Const(:((%11)(%13)))
4 β %15 = Main.tmapreduce::Core.Const(Main.tmapreduce)
β %16 = Main.:(var"#test_custom##0#test_custom##1")::Core.Const(var"#test_custom##0#test_custom##1")
β %17 = Core._typeof_captured_variable(x)::Core.Const(Tuple{Float64, Float64, Float64})
β %18 = Core._typeof_captured_variable(y)::Core.Const(Tuple{Float64, Float64, Float64})
β %19 = Core.apply_type(%16, %17, %18)::Core.Const(var"#test_custom##0#test_custom##1"{Tuple{Float64, Float64, Float64}, Tuple{Float64, Float64, Float64}})
β (#17 = %new(%19, x, y))
β %21 = #17::var"#test_custom##0#test_custom##1"{Tuple{Float64, Float64, Float64}, Tuple{Float64, Float64, Float64}}
β %22 = Main.:(var"#test_custom##2#test_custom##3")::Core.Const(var"#test_custom##2#test_custom##3")
β (#18 = %new(%22))
β %24 = #18::Core.Const(var"#test_custom##2#test_custom##3"())
β %25 = Main.tuple_eachindex::Core.Const(Main.tuple_eachindex)
β %26 = (%25)(x)::Core.Const((1, 2, 3))
β %27 = (%15)(%21, %24, %26)::Tuple{Float64, Tuple{Float64, Float64, Float64}}
βββ return %27
I was wondering if there are Base Julia functions that do this. I mean, this should be supported by the Julia Base library I would say.