如何在Julia中指定函数的类型/签名?

时间:2017-09-03 21:40:09

标签: methods types julia signature

我正在预编译库中实现Newton's method for finding roots。常见的情况是使用从Float64Float64的函数,我希望它的优化编译版本存在于库中。当然,我也会实现类型泛型版本,但Julia需要通过签名来区分方法,以便知道在运行时调用哪一个。目前,实施是:

#Float64 optimized version
function findrootNewton( func, funcder, guess::Float64,
                        rtol::Float64=1e-12, abstol::Float64=1e-12, maxiter::Int=100 )
    #make sure tolerances are positive
    if rtol <= 0.0
        error("findrootNewton: rtol must be a positive number")
    end

    if abstol <= 0.0
        error("findrootNewton: abstol must be a positive number")
    end

    if maxiter <= 0.0
        error("findrootNewton: maxiter must be a positive integer")
    end

    converged::Bool = false

    oldx::Float64 = guess
    newx::Float64 = oldx - ((func(oldx) / funcder(oldx))::Float64)
    absdiff = abs(oldx - newx)

    iter = 2
    while (absdiff < abstol || absdiff < rtol * abs(newx)) && iter <= maxiter
        oldx = newx
        newx = oldx - func(oldx) / funcder(oldx)
        absdiff = abs(oldx - newx)

        iter += 1
    end #while (absdiff < abstol || absdiff < rtol * abs(newx)) && newxiter <= maxiter

    if iter <= maxiter
        converged = true
    end

    return (newx, converged)
end #findzeroNewton

#Generic version
function findrootNewton( func, funcder, guess::Number,
                        rtol::Real=1e-12, abstol::Real=1e-12, maxiter::Int=100 )
    #make sure tolerances are positive
    if rtol <= 0
        error("findrootNewton: rtol must be a positive number")
    end

    if abstol <= 0
        error("findrootNewton: abstol must be a positive number")
    end

    if maxiter <= 0
        error("findrootNewton: maxiter must be a positive integer")
    end

    converged::Bool = false

    newx = oldx - func(oldx) / funcder(oldx)
    oldx = convert(typeof(newx), guess)
    absdiff = abs(oldx - newx)

    iter = 2
    while (absdiff < abstol || absdiff < rtol * abs(newx)) && iter <= maxiter
        oldx = newx
        newx = oldx - func(oldx) / funcder(oldx)
        absdiff = abs(oldx - newx)

        iter += 1
    end #while (absdiff < abstol || absdiff < rtol * abs(newx)) && newxiter <= maxiter

    if iter <= maxiter
        converged = true
    end

    return (newx, converged)
end #findzeroNewton

尚未使用/调试。例如,我没有检查导数是否达到零,因为我正在为此编码的特定情况不需要它。

请注意,如果guessrtolabstol参数为Float64,但函数不会返回Float64但{{1}然后,代码将在BigFloat定义中的类型断言点失败,即使有一个适用于通用函数的方法。有没有办法避免这个问题?

编辑: 我可以指定变量存储在Julia中的数据类型,例如:

newx

是否可以类似地指定可以存储(指向)函数的变量的签名?

2 个答案:

答案 0 :(得分:3)

为清楚起见,我会将我的评论作为答案写下: 除非要在函数体中实现专门的处理,否则您不需要在Julia中的类型上专门化函数签名。参数类型断言对代码速度或可编译性没有影响。见http://docs.julialang.org/en/latest/manual/performance-tips/

julia中函数参数中的类型断言主要用于控制多个调度,即不同类型的输入参数的不同函数行为。如果没有声明类型,编译器将自动为每个输入参数组合编译一个类型专用版本。

如果您需要其他原因,例如为了确保类型稳定性,断言函数的返回类型与输入相同,你可以做

function foo(x::T)::T where T
...
end

答案 1 :(得分:1)

这不是编写Julia代码的方法。您正在编写Julia,就好像它是静态类型的语言一样。这是一个容易犯的错误,因为Julia看起来很像静态类型的语言。要在Julia中获得性能,重要的不是使用类型进行注释,而是实现类型稳定性。

这意味着编写代码,以便在执行一段代码后,变量的类型不会改变。 Julia提供了许多功能和设施来帮助您完成此任务,例如zeroiszerosimilar等。

只需使用您的通用函数,它将具有与“专用”函数相同的性能。在Julia中,针对特定类型的专用功能仅在您需要其他算法时才有意义。例如,intersect(circle, triangle)需要与intersect(circle, circle)不同的代码。但是,对于使用32位浮点数和使用64位浮点数的圆,您不会编写其他方法。


要给出具体建议,请允许我对您编写的一些代码进行评论。

 if rtol <= 0.0
        error("findrootNewton: rtol must be a positive number")
 end

对于通用版本,最好按优先顺序编写以下版本之一:

  1. if rtol <= zero(rtol)
  2. if rtol <= zero(T),其中Trtol的类型
  3. if rtol <= 0

因为这样可以确保您正在比较同类型的数目,从而避免了类型转换。整数0优于浮点数0.0,因为它通常会减少类型转换/促销。

您可能需要使用throw(DomainError(rtol, "findrootNewton: rtol must be a positive number"))来表示其中一个函数参数不在域之内。


  

我可以指定变量存储在Julia中的数据类型,例如   例子

x::Float64 = 2.5

这是不必要的,而且是错误的思考方式。请记住,Julia不是静态类型的语言。如果您是C / C ++开发人员,则可以将变量视为大小不同的小内存盒,它们可以容纳浮点数,整数或布尔值。然后,此分配意味着将2.5放入64位浮点数框中。

但是,这不是动态语言(如Julia)中发生的情况。从概念上讲,您将创建一个浮点对象2.5,并在其上粘贴标签x。分配方式与您所想的相反。您没有将数字分配给名为x的框。而是将标签x粘贴到数字2.5上。所以写这样的东西:

converged::Bool = false

完全没有必要。您不确定是否将false放入布尔大小的框中。没有盒子。相反,您将标签converged粘贴在false对象上,然后断言标签converged已附加到布尔对象。这没有任何性能或内存优势。


您仅在常规版本中执行此行,并且可以假定您认为这与在Float64版本中相比性能较低。

oldx = convert(typeof(newx), guess)

但是,对converttypeof的调用没有任何区别。在所有类型都匹配的理想情况下,这些调用会被优化掉。看这个简单的例子:

julia> foobar(a, b) = convert(typeof(a), a + b)
foobar

julia> @code_warntype foobar(1, 1)
Body::Int64
1 ─ %1 = (Base.add_int)(a, b)::Int64
└──      return %1

julia> @code_warntype foobar(1.0, 1.0)
Body::Float64
1 ─ %1 = (Base.add_float)(a, b)::Float64
└──      return %1

您可以看到类型匹配时,Julia JIT会将其简化为简单的整数或浮点数。

如果您不确定以一种方式编写函数对性能的影响,建议您习惯使用@code_warntype@code_llvm@code_native宏。它们为您提供了有关Julia在给定特定参数集的情况下如何转换代码的宝贵信息。

关于您是否可以为函数签名创建类型断言的问题。您目前无法在Julia中做到这一点。在您的情况下,则不需要。

但是,解决方法通常涉及使用某些基于特征的方法。您可以考虑将Newton方法的参数转换为类型,然后在其上分配输入。