我正在使用库 ValidatedNumerics 进行一些间隔计算,并且我正在尝试优化我的代码。我定义了以下内容:
using ValidatedNumerics
immutable Vector2D{T}
x::Complex{ValidatedNumerics.Interval{T}}
y::Complex{ValidatedNumerics.Interval{T}}
end
function F(x::Complex{ValidatedNumerics.Interval{BigFloat}},y::Complex{ValidatedNumerics.Interval{BigFloat}})
g = 3(1+(1+x+y)^2)/4
Vector2D{BigFloat}(x + 2y + g,y + g)
end
然后使用以下给出正确的答案
x = @biginterval(1+1im)
F(x,x)
但是,当我输入:
@code_warntype F(x,x)
我得到:
Variables:
#self#::#F
x::Complex{ValidatedNumerics.Interval{BigFloat}}
y::Complex{ValidatedNumerics.Interval{BigFloat}}
g::Complex{T<:Real}
Body:
begin
# meta: location operators.jl + 138
# meta: location complex.jl + 162
SSAValue(0) = (Core.getfield)(x::Complex{ValidatedNumerics.Interval{BigFloat}},:re)::ValidatedNumerics.Interval{BigFloat}
SSAValue(2) = $(Expr(:invoke, LambdaInfo for +(::ValidatedNumerics.Interval{BigFloat}, ::ValidatedNumerics.Interval{BigFloat}), :(Base.+), :($(Expr(:invoke, LambdaInfo for ValidatedNumerics.Interval{BigFloat}(::BigFloat, ::BigFloat), ValidatedNumerics.Interval{BigFloat}, :($(Expr(:invoke, LambdaInfo for _convert_rounding(::Type{BigFloat}, ::Int64, ::RoundingMode{:Down}), :(Base.Rounding._convert_rounding), BigFloat, 1, :(ValidatedNumerics.RoundDown)))), :($(Expr(:invoke, LambdaInfo for _convert_rounding(::Type{BigFloat}, ::Int64, ::RoundingMode{:Up}), :(Base.Rounding._convert_rounding), BigFloat, 1, :(ValidatedNumerics.RoundUp))))))), :($(Expr(:invoke, LambdaInfo for ValidatedNumerics.Interval{BigFloat}(::BigFloat, ::BigFloat), ValidatedNumerics.Interval{BigFloat}, :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Down}), BigFloat, :((Core.getfield)(SSAValue(0),:lo)::BigFloat), :(ValidatedNumerics.RoundDown)))), :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Up}), BigFloat, :((Core.getfield)(SSAValue(0),:hi)::BigFloat), :(ValidatedNumerics.RoundUp)))))))))
SSAValue(1) = (Core.getfield)(x::Complex{ValidatedNumerics.Interval{BigFloat}},:im)::ValidatedNumerics.Interval{BigFloat}
# meta: pop location
SSAValue(7) = $(Expr(:invoke, LambdaInfo for ValidatedNumerics.Interval{BigFloat}(::BigFloat, ::BigFloat), ValidatedNumerics.Interval{BigFloat}, :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Down}), BigFloat, :((Core.getfield)(SSAValue(2),:lo)::BigFloat), :(ValidatedNumerics.RoundDown)))), :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Up}), BigFloat, :((Core.getfield)(SSAValue(2),:hi)::BigFloat), :(ValidatedNumerics.RoundUp))))))
SSAValue(8) = $(Expr(:invoke, LambdaInfo for ValidatedNumerics.Interval{BigFloat}(::BigFloat, ::BigFloat), ValidatedNumerics.Interval{BigFloat}, :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Down}), BigFloat, :((Core.getfield)(SSAValue(1),:lo)::BigFloat), :(ValidatedNumerics.RoundDown)))), :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Up}), BigFloat, :((Core.getfield)(SSAValue(1),:hi)::BigFloat), :(ValidatedNumerics.RoundUp))))))
# meta: location complex.jl + 125
SSAValue(5) = $(Expr(:invoke, LambdaInfo for +(::ValidatedNumerics.Interval{BigFloat}, ::ValidatedNumerics.Interval{BigFloat}), :(Base.+), SSAValue(7), :((Core.getfield)(y,:re)::ValidatedNumerics.Interval{BigFloat})))
SSAValue(4) = $(Expr(:invoke, LambdaInfo for +(::ValidatedNumerics.Interval{BigFloat}, ::ValidatedNumerics.Interval{BigFloat}), :(Base.+), SSAValue(8), :((Core.getfield)(y,:im)::ValidatedNumerics.Interval{BigFloat})))
# meta: pop location
SSAValue(6) = $(Expr(:new, Complex{ValidatedNumerics.Interval{BigFloat}}, :($(Expr(:invoke, LambdaInfo for ValidatedNumerics.Interval{BigFloat}(::BigFloat, ::BigFloat), ValidatedNumerics.Interval{BigFloat}, :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Down}), BigFloat, :((Core.getfield)(SSAValue(5),:lo)::BigFloat), :(ValidatedNumerics.RoundDown)))), :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Up}), BigFloat, :((Core.getfield)(SSAValue(5),:hi)::BigFloat), :(ValidatedNumerics.RoundUp))))))), :($(Expr(:invoke, LambdaInfo for ValidatedNumerics.Interval{BigFloat}(::BigFloat, ::BigFloat), ValidatedNumerics.Interval{BigFloat}, :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Down}), BigFloat, :((Core.getfield)(SSAValue(4),:lo)::BigFloat), :(ValidatedNumerics.RoundDown)))), :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Up}), BigFloat, :((Core.getfield)(SSAValue(4),:hi)::BigFloat), :(ValidatedNumerics.RoundUp)))))))))
# meta: pop location
SSAValue(9) = (3 * (1 + (Core._apply)(Base.^,$(Expr(:invoke, LambdaInfo for promote(::Complex{ValidatedNumerics.Interval{BigFloat}}, ::Complex{Int64}), :(Base.promote), SSAValue(6), :($(Expr(:new, Complex{Int64}, 2, 0))))))::Complex{T<:Real})::Complex{T<:Real})::Complex{T<:Real}
g::Complex{T<:Real} = (Base.Complex)(((Core.getfield)(SSAValue(9),:re)::Real / 4)::Any,((Core.getfield)(SSAValue(9),:im)::Real / 4)::Any)::Complex{T<:Real} # line 3:
# meta: location complex.jl * 170
SSAValue(11) = (Core.getfield)(y::Complex{ValidatedNumerics.Interval{BigFloat}},:re)::ValidatedNumerics.Interval{BigFloat}
SSAValue(12) = $(Expr(:invoke, LambdaInfo for *(::ValidatedNumerics.Interval{BigFloat}, ::ValidatedNumerics.Interval{BigFloat}), :(Base.*), :($(Expr(:invoke, LambdaInfo for ValidatedNumerics.Interval{BigFloat}(::BigFloat, ::BigFloat), ValidatedNumerics.Interval{BigFloat}, :($(Expr(:invoke, LambdaInfo for _convert_rounding(::Type{BigFloat}, ::Int64, ::RoundingMode{:Down}), :(Base.Rounding._convert_rounding), BigFloat, 2, :(ValidatedNumerics.RoundDown)))), :($(Expr(:invoke, LambdaInfo for _convert_rounding(::Type{BigFloat}, ::Int64, ::RoundingMode{:Up}), :(Base.Rounding._convert_rounding), BigFloat, 2, :(ValidatedNumerics.RoundUp))))))), :($(Expr(:invoke, LambdaInfo for ValidatedNumerics.Interval{BigFloat}(::BigFloat, ::BigFloat), ValidatedNumerics.Interval{BigFloat}, :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Down}), BigFloat, :((Core.getfield)(SSAValue(11),:lo)::BigFloat), :(ValidatedNumerics.RoundDown)))), :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Up}), BigFloat, :((Core.getfield)(SSAValue(11),:hi)::BigFloat), :(ValidatedNumerics.RoundUp)))))))))
SSAValue(10) = (Core.getfield)(y::Complex{ValidatedNumerics.Interval{BigFloat}},:im)::ValidatedNumerics.Interval{BigFloat}
SSAValue(13) = $(Expr(:invoke, LambdaInfo for *(::ValidatedNumerics.Interval{BigFloat}, ::ValidatedNumerics.Interval{BigFloat}), :(Base.*), :($(Expr(:invoke, LambdaInfo for ValidatedNumerics.Interval{BigFloat}(::BigFloat, ::BigFloat), ValidatedNumerics.Interval{BigFloat}, :($(Expr(:invoke, LambdaInfo for _convert_rounding(::Type{BigFloat}, ::Int64, ::RoundingMode{:Down}), :(Base.Rounding._convert_rounding), BigFloat, 2, :(ValidatedNumerics.RoundDown)))), :($(Expr(:invoke, LambdaInfo for _convert_rounding(::Type{BigFloat}, ::Int64, ::RoundingMode{:Up}), :(Base.Rounding._convert_rounding), BigFloat, 2, :(ValidatedNumerics.RoundUp))))))), :($(Expr(:invoke, LambdaInfo for ValidatedNumerics.Interval{BigFloat}(::BigFloat, ::BigFloat), ValidatedNumerics.Interval{BigFloat}, :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Down}), BigFloat, :((Core.getfield)(SSAValue(10),:lo)::BigFloat), :(ValidatedNumerics.RoundDown)))), :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Up}), BigFloat, :((Core.getfield)(SSAValue(10),:hi)::BigFloat), :(ValidatedNumerics.RoundUp)))))))))
# meta: pop location
SSAValue(22) = $(Expr(:invoke, LambdaInfo for ValidatedNumerics.Interval{BigFloat}(::BigFloat, ::BigFloat), ValidatedNumerics.Interval{BigFloat}, :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Down}), BigFloat, :((Core.getfield)(SSAValue(12),:lo)::BigFloat), :(ValidatedNumerics.RoundDown)))), :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Up}), BigFloat, :((Core.getfield)(SSAValue(12),:hi)::BigFloat), :(ValidatedNumerics.RoundUp))))))
SSAValue(23) = $(Expr(:invoke, LambdaInfo for ValidatedNumerics.Interval{BigFloat}(::BigFloat, ::BigFloat), ValidatedNumerics.Interval{BigFloat}, :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Down}), BigFloat, :((Core.getfield)(SSAValue(13),:lo)::BigFloat), :(ValidatedNumerics.RoundDown)))), :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Up}), BigFloat, :((Core.getfield)(SSAValue(13),:hi)::BigFloat), :(ValidatedNumerics.RoundUp))))))
# meta: location operators.jl + 138
# meta: location complex.jl + 125
SSAValue(16) = $(Expr(:invoke, LambdaInfo for +(::ValidatedNumerics.Interval{BigFloat}, ::ValidatedNumerics.Interval{BigFloat}), :(Base.+), :((Core.getfield)(x,:re)::ValidatedNumerics.Interval{BigFloat}), SSAValue(22)))
SSAValue(15) = $(Expr(:invoke, LambdaInfo for +(::ValidatedNumerics.Interval{BigFloat}, ::ValidatedNumerics.Interval{BigFloat}), :(Base.+), :((Core.getfield)(x,:im)::ValidatedNumerics.Interval{BigFloat}), SSAValue(23)))
# meta: pop location
SSAValue(19) = $(Expr(:invoke, LambdaInfo for ValidatedNumerics.Interval{BigFloat}(::BigFloat, ::BigFloat), ValidatedNumerics.Interval{BigFloat}, :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Down}), BigFloat, :((Core.getfield)(SSAValue(16),:lo)::BigFloat), :(ValidatedNumerics.RoundDown)))), :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Up}), BigFloat, :((Core.getfield)(SSAValue(16),:hi)::BigFloat), :(ValidatedNumerics.RoundUp))))))
SSAValue(20) = $(Expr(:invoke, LambdaInfo for ValidatedNumerics.Interval{BigFloat}(::BigFloat, ::BigFloat), ValidatedNumerics.Interval{BigFloat}, :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Down}), BigFloat, :((Core.getfield)(SSAValue(15),:lo)::BigFloat), :(ValidatedNumerics.RoundDown)))), :($(Expr(:invoke, LambdaInfo for BigFloat(::BigFloat, ::RoundingMode{:Up}), BigFloat, :((Core.getfield)(SSAValue(15),:hi)::BigFloat), :(ValidatedNumerics.RoundUp))))))
SSAValue(18) = (Base.Complex)((SSAValue(19) + (Core.getfield)(g::Complex{T<:Real},:re)::Real)::Any,(SSAValue(20) + (Core.getfield)(g::Complex{T<:Real},:im)::Real)::Any)::Complex{T<:Real}
# meta: pop location
SSAValue(21) = (Base.Complex)(((Core.getfield)(y::Complex{ValidatedNumerics.Interval{BigFloat}},:re)::ValidatedNumerics.Interval{BigFloat} + (Core.getfield)(g::Complex{T<:Real},:re)::Real)::Any,((Core.getfield)(y::Complex{ValidatedNumerics.Interval{BigFloat}},:im)::ValidatedNumerics.Interval{BigFloat} + (Core.getfield)(g::Complex{T<:Real},:im)::Real)::Any)::Complex{T<:Real}
return $(Expr(:new, Vector2D{BigFloat}, :($(Expr(:new, Complex{ValidatedNumerics.Interval{BigFloat}}, :((Base.convert)(ValidatedNumerics.Interval{BigFloat},(Core.getfield)(SSAValue(18),:re)::Real)), :((Base.convert)(ValidatedNumerics.Interval{BigFloat},(Core.getfield)(SSAValue(18),:im)::Real))))), :($(Expr(:new, Complex{ValidatedNumerics.Interval{BigFloat}}, :((Base.convert)(ValidatedNumerics.Interval{BigFloat},(Core.getfield)(SSAValue(21),:re)::Real)), :((Base.convert)(ValidatedNumerics.Interval{BigFloat},(Core.getfield)(SSAValue(21),:im)::Real)))))))
end::Vector2D{BigFloat}
其中,以下是红色的:
::Complex{T<:Real}
::Real
::Any
这是否真的意味着我的定义类型不稳定?我该如何解决?
最佳答案
正如@David Sanders 指出的 SSAValue(9)
是 F
问题的根源。进一步挖掘(接下来是相当多的挖掘),是 ^2
混淆了类型推断。 ^
参数的类型签名是
Complex{ValidatedNumerics.Interval{BigFloat}}, Int64
在将
^(Complex,Complex)
转换为 2
后调用 Complex{Int64}(2,0)
。反过来,^(Complex,Complex)
将 Complex{Int64}
提升为 Complex{ValidatedNumerics.Interval{BigFloat}}
。现在,我们将在 ^(T<:Complex,T<:Complex)
中的 complex.jl:506
中进行一些实际计算。这是一个代码片段,其中标记了一些行:
function ^{T<:Complex}(z::T, p::T)
if isinteger(p)
rp = real(p) # <---------- (1)
if rp < 0
return power_by_squaring(inv(float(z)), convert(Integer, -rp))
else
return power_by_squaring(float(z), convert(Integer, rp))
end
end
pr, pim = reim(p)
zr, zi = reim(z)
r = abs(z) # <---------- (*)
rp = r^pr # <---------- (2)
theta = atan2(zi, zr)
这个函数有两个执行分支,第一个,当幂是一个整数时,第二个当它不是。两个分支都使用
rp
变量并将其分配给不同的类型,从而将 rp
的类型推断为 Any。第二个问题是标记为 (*)
的行。请注意,此行实际上并未在计算中执行,因为幂是整数。如果它被执行,问题不会停留在类型推断级别,而是变成堆栈溢出。abs(z)
当 z
为 Complex{T} 时,(*)
会在实部和虚部调用 hypot(T,T)
。这些依次调用 sqrt(float(T))
(对于毕达哥拉斯定理)。这在 complex.jl:320
中定义为:sqrt(z::Complex) = sqrt(float(z))
由于
float(Complex{ValidatedNumerics.Interval{BigFloat}})
是 Complex{ValidatedNumerics.Interval{Float64}}
,这仍然调用 sqrt(Complex)
导致循环和堆栈溢出(这次不是这个站点的名称)。这种自循环还会导致 Union{}
类型推断,这也会导致调用函数也无法进行类型推断。总而言之,
complex.jl
有更仔细的实现和测试的空间。这是我现在有时间的所有挖掘,但如果需要澄清或修复,欢迎提出意见。
实际上,要解决这个问题,对
::
的结果使用 ^
的显式类型断言可能会解决 F
的问题。关于types - 有人可以指出我的代码中的类型不稳定吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40413402/