Skip to content

Commit

Permalink
add complex polygamma and Hurwitz zeta functions (fixes #7033)
Browse files Browse the repository at this point in the history
  • Loading branch information
stevengj committed Jun 5, 2014
1 parent ff6bde7 commit 76a7335
Show file tree
Hide file tree
Showing 4 changed files with 348 additions and 299 deletions.
27 changes: 27 additions & 0 deletions base/math.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,33 @@ macro horner(x, p...)
ex
end

# Evaluate p[1] + z*p[2] + z^2*p[3] + ... + z^(n-1)*p[n], assuming
# the p[k] are *real* coefficients. This uses Horner's method if z
# is real, but for complex z it uses a more efficient algorithm described
# in Knuth, TAOCP vol. 2, section 4.6.4, equation (3).
macro chorner(z, p...)
a = :($(esc(p[end])))
b = :($(esc(p[end-1])))
as = {}
for i = length(p)-2:-1:1
ai = symbol(string("a", i))
push!(as, :($ai = $a))
a = :($b + r*$ai)
b = :($(esc(p[i])) - s * $ai)
end
ai = :a0
push!(as, :($ai = $a))
C = Expr(:block,
:(x = real($(esc(z)))),
:(y = imag($(esc(z)))),
:(r = x + x),
:(s = x*x + y*y),
as...,
:(Complex($ai * x + $b, $ai * y)))
R = Expr(:macrocall, symbol("@horner"), esc(z), p...)
:(isa($(esc(z)), Real) ? $R : $C)
end

rad2deg(z::Real) = oftype(z, 57.29577951308232*z)
deg2rad(z::Real) = oftype(z, 0.017453292519943295*z)
rad2deg(z::Integer) = rad2deg(float(z))
Expand Down
Loading

8 comments on commit 76a7335

@StefanKarpinski
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make any sense to roll the @chorner code into @horner?

@stevengj
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like there might be cases where you might not want the @chorner algorithm, so I opted for caution for the time being.

e.g. I'm not sure how it performs for complex coefficients, but the algorithm certainly wasn't designed for that case. I suppose you could try to add more isa statements to detect that case.

@stevengj
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it seems like it is still significantly faster than @horner for complex coefficients (and is still correct).

Now that I think about it, this makes sense since the algorithm is of course linear in the coefficients.

@stevengj
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also probably call it @evalpoly rather than @horner, since it is not really Horner's method.

Then we can export @evalpoly and leave @horner there for people who need it, or for backwards compatibility (@horner is currently used in Distributions.jl).

@stevengj
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #7146

@StefanKarpinski
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it have another name? The person who invented the technique?

@StefanKarpinski
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the good thing about @evalpoly is that it implies nothing about how you do it, just that you want to do that, so the implementation is free to choose whatever technique is most efficient. Horner's method for real values, this method for complex.

@stevengj
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Knuth doesn't give it a name. And the source he cites for it is a final-exam problem from the University of Bergen, which also doesn't name the technique.

It is also closely related to the Goetzel algorithm, but that algorithm is specialized to |z| == 1 (Fourier series), in which case you can save another half of the multiplications.

Please sign in to comment.