Skip to content

Make pochhammer more robust #72

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jun 21, 2019

Conversation

putianyi889
Copy link
Contributor

@putianyi889 putianyi889 commented Jun 15, 2019

pochhammer can now deal with arbitrary ::Number without throwing unexpected errors.
I did this by adding a modified version of gamma function named newgamma which returns Inf at negative integers.

Added a test file.

Edit: following @dlfivefifty 's suggestion, I used ogamma which returns 0 at negative integers.

@dlfivefifty
Copy link
Member

Make sure your tests pass on Travis. I believe you forgot an import FastTransforms: pochhammer.

Also, it would be more natural to have a special function specifically for 1/gamma(x), say called ogamma, instead of relying on floating point Inf calculations. I'm surprised this doesn't already exist (from what I can tell).

@MikaelSlevinsky
Copy link
Member

Thanks for taking a look at this! Special functions are always tricky to implement and my early version of the Pochhammer symbol is quite naive. While we're at it, we may as well consider all cases. Let me start mapping out some cases that come to mind for real numbers.

Formally, (x)n = gamma(x+n)/gamma(x), but this runs into trouble when x or x+n (or both!) are near a negative integer.

For x < 0, we may use the Euler reflection formula, gamma(x)gamma(1-x) = pi/sinpi(x) to relate a Pochhamer evaluation to a positive argument call (n >= 0):

(-x)n = sinpi(x)/sinpi(x-n) (x-n+1)n

This formula is a start, but more details arise in the implementation of the ratio of sines (or cardinal sines), especially as n tends to an integer. Fortunately, when n is an integer, this reduces to the more well-known formula (-x)n = (-1)n (x-n+1)n. Otherwise, two options include argument reduction (to bring both arguments as close as possible), and the sine addition formula.

Now, if x+n is a negative integer and x is not, then I think the correct output is arguably analogous to:

julia> log2(-1.0)
ERROR: DomainError with -1.0:
NaN result for non-NaN input.

because gamma(x) is then just a constant finite scaling of gamma(x+n) which is evaluated at a simple pole. Since the pole is simple, the infinite limit depends on the direction of approach, so we cannot simply settle for one Inf or the other. Therefore, NaN seems appropriate, and to conform with Julia's other special functions would suggest returning a DomainError.

Finally, if x+n and x are both negative integers, there is a ratio of residues that we should be able to reason with. For example, if n::Integer >= 0 we may safely use (-x)n = (-1)n (x-n+1)n, but if n<0 then we may want to use (x)n = 1/(x+n)-n first.

@codecov
Copy link

codecov bot commented Jun 16, 2019

Codecov Report

Merging #72 into master will increase coverage by 0.1%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master      #72     +/-   ##
=========================================
+ Coverage    52.3%   52.41%   +0.1%     
=========================================
  Files          33       33             
  Lines        3185     3188      +3     
=========================================
+ Hits         1666     1671      +5     
+ Misses       1519     1517      -2
Impacted Files Coverage Δ
src/specialfunctions.jl 74.32% <100%> (+0.83%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d2add70...8336ae1. Read the comment docs.

@codecov
Copy link

codecov bot commented Jun 16, 2019

Codecov Report

Merging #72 into master will increase coverage by 20.04%.
The diff coverage is 100%.

Impacted file tree graph

@@             Coverage Diff             @@
##           master      #72       +/-   ##
===========================================
+ Coverage    52.3%   72.35%   +20.04%     
===========================================
  Files          33       33               
  Lines        3185     2308      -877     
===========================================
+ Hits         1666     1670        +4     
+ Misses       1519      638      -881
Impacted Files Coverage Δ
src/specialfunctions.jl 83.22% <100%> (+9.72%) ⬆️
src/clenshawcurtis.jl 60% <0%> (-15%) ⬇️
src/TriangularHarmonics/slowplan.jl 0% <0%> (ø) ⬆️
src/TriangularHarmonics/trifunctions.jl 0% <0%> (ø) ⬆️
src/chebyshevtransform.jl 97% <0%> (+4.61%) ⬆️
src/cjt.jl 76.71% <0%> (+6.71%) ⬆️
src/hierarchical.jl 48.06% <0%> (+6.83%) ⬆️
src/SphericalHarmonics/sphfunctions.jl 78.94% <0%> (+13.72%) ⬆️
src/SphericalHarmonics/slowplan.jl 46.79% <0%> (+14.35%) ⬆️
... and 20 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d2add70...055248f. Read the comment docs.

@@ -61,6 +61,14 @@ function pochhammer(x::Number,n::UnitRange{T}) where T<:Real
ret
end

function ogamma(x::Number)
if isinteger(x) && x<0
0.0
Copy link
Member

Choose a reason for hiding this comment

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

This should be type stable: zero(float(x))

Copy link
Member

Choose a reason for hiding this comment

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

Also, why not x ≤ 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because the original gamma already returns Inf
gamma(0.0)=Inf
gamma(-0.0)=-Inf

if isinteger(x) && x<0
0.0
else
1.0/gamma(x)
Copy link
Member

Choose a reason for hiding this comment

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

inv(gamma(x)) is cleaner.

@test pochhammer(-1,2) == 0
@test pochhammer(-5,3) == -60
@test pochhammer(-1,-0.5) == 0
@test 1.0/pochhammer(-0.5,-0.5) == 0
Copy link
Member

Choose a reason for hiding this comment

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

Add complex tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

gamma is nonzero and holomorphic except the singularities, so complex tests don't make any difference, I suppose. I skipped array tests, though. Maybe add some array tests

Copy link
Member

Choose a reason for hiding this comment

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

I mean complex types but at integers: ogamma(-1+0im)

@test pochhammer(-1,-0.5) == 0
@test 1.0/pochhammer(-0.5,-0.5) == 0
@test pochhammer(-1+0im,-1) == -2
@test pochhammer([2,-5,-1],3) == [24,-60,0]
Copy link
Member

Choose a reason for hiding this comment

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

vector-valued arguments should be deprecated in favour of pochhammer.([2,-5,-1],3), so perhaps remove this test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So can pochhammer(x::AbstractArray{T},n::Integer) be removed? Do abstractarrays work with dot operations?

Copy link
Member

Choose a reason for hiding this comment

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

It can be deprecated, though that can be done separately. Yes abstract arrays support broadcastibg

@dlfivefifty dlfivefifty merged commit 3e9c187 into JuliaApproximation:master Jun 21, 2019
putianyi889 added a commit to putianyi889/FastTransforms.jl that referenced this pull request Jun 22, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants