-
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
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
BUG: special.hyp2f1 now allows neg int c value #8110
Conversation
X=DBLE(Z) | ||
Y=DIMAG(Z) | ||
EPS=1.0D-15 | ||
ISFER=0 | ||
L0=C.EQ.INT(C).AND.C.LT.0.0D0 | ||
L0=C.EQ.INT(C).AND.C.LE.0.0D0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm... this problem was here already, but checking C.EQ.INT(C)
is bad practice because INT(C)
overflows before C
does. The floor
check in the C wrapper is better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be better to use a bigger sized integer in the fortran code? Something like IDINT? Or does the floor
function itself have better overflow protections?
My only concern here is with duplicating code in both the wrapper file and the fortran file which seems like it would make the code base hard to follow and maintain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But to be clear I can also re-produce my checks in the c wrapper code if that's what needs to be done...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or does the floor function itself have better overflow protections?
The idea is that in C floor
returns a double, so overflow is not an issue. The closest Fortran functions CEILING
and FLOOR
return integers. I don't know of a way to round in Fortran that returns REAL(8)
.
So I think this change is fine; the code was already doing the checks like that anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for clarification, you think my change is fine. I do not need to revert the safety checks that I removed from specfun_wrappers.c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for clarification, you think my change is fine. I do not need to revert the safety checks that I removed from specfun_wrappers.c
Yes.
Could you add test cases? They should be here: https://github.com/scipy/scipy/blob/master/scipy/special/tests/test_mpmath.py#L105 |
I actually do have quite a few test cases inside of a txt file that I was going to add to the folder: These test cases would be loaded into But I haven't added them to this PR, because the file also includes a lot of test cases for my upcoming PR that fixes #8054 . But I can either add a smaller txt file with just the c=-n test cases as described above, or I can add test cases to the file you linked. I'm fine either way. |
I think we try to only add to If you do end up adding to |
scipy/special/tests/test_mpmath.py
Outdated
|
||
# Test both the complex and real versions of hyp2f1 | ||
for z in (0.5+0.5j,0.5): | ||
curr_pts=[p+(z,) for p in pts] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the Pep 8 checker is failing because of the lack of whitespace around the =
. Remember to pip install pep8
and run pep8 scipy/special
to check style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, remember to include spaces between function arguments and around operators like +
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@person142 Okay cool! I did not know about Pep 8...
scipy/special/tests/test_mpmath.py
Outdated
(1.23, -4, -4), | ||
(2.12, 0, -3), | ||
(-2, 0.89, -4) | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Less indentation on the ]
.
scipy/special/tests/test_mpmath.py
Outdated
scipy_values=[sc.hyp2f1(*p) for p in curr_pts] | ||
scipy_values=np.array(scipy_values, dtype=curr_type) | ||
|
||
assert_allclose(scipy_values,mp_values,rtol=1.e-10) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Look at the uses of FuncData
elsewhere in the module; probably best to use the same pattern here. (You will get better formatted error reporting, which is always nice.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried doing that, but the problem was that FuncData requires a single array of a single type in order to evaluate the function. In the case of hyp2f1, I would need to pass in an array that contains both real numbers (a,b,c) and complex numbers (z,mpmath's value). (Note that I can't just pass in a single array of complex numbers since this array can't be passed to hyp2f1 since it doesn't match hyp2f1's signature of real,real,real,complex).
Now I can pass a single array of real numbers (basically the columns=a,b,c,z_real,z_imag,expected_real,expected_imag) and pass in a wrapper function to FuncData that converts this array into something that can be passed to hyp2f1. This is what I did originally did with my text file of 4000 or so test cases. But I was just afraid that people might think that was overkill for just checking 12 or so values....
So let me know what you prefer. I already have code that uses FuncData; it's just a matter of cut and paste...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I vote FuncData, whenever assert_allclose
fails you have to go hack up the test to figure out what actually went wrong. The other option (probably more common in the tests) is to use the columns a, b, c, z, expected
where you make a
, b
, and c
complex and write a wrapper that does hyp2f1(a.real, b.real, c.real, z)
.
I've fixed the PEP 8 style issues. @person142 I can also use FuncData to run the test cases if that's what needs to be done; see my reply to your previous comment. |
@person142 Fixed tests so that they use FuncData |
Fixed a small bug that was failing one of the tests in the 'continuous-integration/travis-ci/pr...' check... |
Failure due to #8136. |
@@ -6228,6 +6230,14 @@ SUBROUTINE HYGFZ(A,B,C,Z,ZHF,ISFER) | |||
CALL GAMMA2(1.0D0+A/2.0D0-B,G2) | |||
CALL GAMMA2(0.5D0+0.5D0*A,G3) | |||
ZHF=G0*G1/(G2*G3) | |||
ELSE IF (L7.AND.((A.EQ.C).OR.(B.EQ.C))) THEN | |||
IF (A.EQ.C) A=BB |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That modifies the input argument --- probably should set AA=BB
and use AA
instead below.
Also, is the intent of if statements that a=c=negint or b=c=negint?
If yes, that does not seem to be guaranteed by the above.
Also, with respect to the comment about changing the values of A and B, note that the original code already changes the values of A and B as required. Before exiting, A and B are returned to their original values (as stored in AA and BB). My code changes follow this convention. |
@FormerPhysicist @pv @person142 No activity here in almost two years--I will probably bump the milestone soon given branching in ~1 week unless you tell me otherwise. |
@FormerPhysicist @person142 Have to bump the milestone again here soon? |
L7=(L3.AND.(DABS(A).LE.DABS(C))) | ||
& .OR.(L4.AND.(DABS(B).LE.DABS(C))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@FormerPhysicist, you write [my emphasis]:
Now if z is complex and c=-m, this PR fixes hyp2f1 so that it returns the correctly computed value if and only if either a or b equals a negative integer -n where n<m.
Shouldn't it then be
L7=(L3.AND.(DABS(A).LE.DABS(C))) | |
& .OR.(L4.AND.(DABS(B).LE.DABS(C))) | |
L7=(L3.AND.(DABS(A).LT.DABS(C))) | |
& .OR.(L4.AND.(DABS(B).LT.DABS(C))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@h-vetinari No, I think it should be LE (less than or equal).
When a=c (or b=c) and both are negative integers, then we need to let the method fall through and hit this section of code.
That part of the code implements formula 15.4.2 from Abramowitz and Stegun.
Otherwise, when a (or b) is a negative integer and abs(a) < abs(c)
, then the hypergeometric function reduces to a polynomial that is analytic everywhere. And again in this case we need to let the method fall through and run the code here
Note that the line you've highlighted is just for detecting infinities. I believe I got those conditions right, but feel free to review them again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the answer. I get that this was for the infinity-detection - what confused me was the relation to the statement in the OP that I quoted, which might be better reflected as (because the n=m part already exists):
Now if z is complex and c=-m, this PR fixes hyp2f1 so that it returns the correctly computed value
if and onlyalso if either a or b equals a negative integer -n where n<m.
Bumping to |
Inactive for the last 6 months so I'll bump the milestone. Also, @steppi is this PR still relevant with your related work in C/Cython? |
This PR fixes several issues associated with the complex z version of hyp2f1 and relating to negative integer values of c:
Previously, if c=0 and z is complex, hyp2f1 would attempt to calculate a value and would return nan+nan*j. This PR fixes it so that (inf + 0j) is returned. This is similar in behavior to what happens when c=0 and z is real (in which case hyp2f1 returns inf).
Now if z is complex and c=-m, this PR fixes hyp2f1 so that it returns the correctly computed value if and only if either a or b equals a negative integer -n where n<m. When c=-m and neither a nor b are negative integers with smaller magnitude than c, then hyp2f1 still returns (inf +0j) as expected.
The code in
specfun_wrappers.c
duplicated one of the safety checks that is already present inspecfun.f
(i.e., the check if z=1 and c-a-b>=0). I removed this code so that HYGFZ is now solely responsible for validating the values of a,b,c.The original issue for this PR was: #7340
Closes #7340
Closes #8119