-
-
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: Fix ellipj when m is near 1.0 (#8480) #8548
base: main
Are you sure you want to change the base?
Conversation
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.
Changes look reasonable to me, I've found Abramowitz and Stegun useful in the past, and the tests appear to validate the functionality. But it would be better if someone with more knowledge in this domain could look!
scipy/special/add_newdocs.py
Outdated
except when `m` is within 1e-9 of 0 or 1. | ||
|
||
In the latter case, when m is near 1, the functions `sn`, `cn`, and `dn` | ||
are found using an approximation for `phi` given by 16.15.4 in [2]. |
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.
[2]_
to make it link properly.
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.
See CircleCI output for example:
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.
@larsoner fixed
scipy/special/add_newdocs.py
Outdated
are found using an approximation for `phi` given by 16.15.4 in [2]. | ||
As this approximation is only valid when `phi < pi/2` (i.e., | ||
when `u<K`), the computation only uses 16.15.4 to find the "principal" | ||
part of `phi` or the remainder `dphi=phi%(pi/2)`. The returned value |
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.
double-backticks on code for it to render properly
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.
Which parts? Do you just mean the dphi=phi%(pi/2) part? Or everywhere I've used one set of backticks (e.g., should phi
just be italicized (one pair of ) or as code (two pairs of
)?)
scipy/special/cephes/ellpj.c
Outdated
/* Evaluate the complete elliptic integral using the | ||
1-m<<1 approximation in ellpk.c */ | ||
K = ellpk(1.0-m); | ||
uabs=fabs(u); |
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.
indentation off by one
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.
fixed
scipy/special/tests/test_mpmath.py
Outdated
|
||
FuncData(dn, dataset, (0, 1), 2, rtol=1e-10).check() | ||
# Add the points that will use the m near 1 method | ||
m = 0.99999999999 |
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.
it's probably worth going a step further. In addition to the one right at the threshold (0.9999999999), how about one just outside it (0.9999999998) and beyond it (0.99999999999)? It helps validate the choice of threshold.
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.
Is it better to write 1. - 1e-10
for readability of the digits?
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.
Sounds good to me.
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.
fixed
Other than the PEP8 whitespace after comma issues in |
Before I review this in earnest:
|
@person142 Done |
Can you comment on your general approach versus e.g. just doing what Boost does (reading their source code is fine): In general argument reduction by irrational numbers (here |
In the latter case, when m is near 1, the functions `sn`, `cn`, and `dn` | ||
are found using an approximation for `phi` given by 16.15.4 in [2]_. | ||
As this approximation is only valid when `phi < pi/2` (i.e., | ||
when `u<K`), the computation only uses 16.15.4 to find the "principal" |
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.
ReST weirdness: single backticks are for references, double backticks for code
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.
Can you elaborate what you mean by 'reference'? Looking at the other function docstrings, it appears that they use a single backtick for variables and math equations (sometimes with the ReST role(?) keyword :math:). Should I change the existing text for ellpj to use double backticks?
I've finally gotten around to looking at the source code for boost version 1.67 implementation of ellpj and there appear to be two main differences with scipy's current implementation:
However, what's also interesting, is that boost had a section that attempted to treat the m close to unity case, but it's been commented out with the comments:
Skimming their code, it looks like they were trying the same naive implementation of 16.15 that scipy does and I wonder if they were getting the same error that my pull request addresses. So the TL;DR: either we stick with AGM for m close to unity, or we can use the modified version of 16.15 proposed by vasilevgmaslov (*Note that AGM seems to be a standard way of getting ellpj for m<1; scipy uses it when m is < 1 and not close to unity.) |
@ilayn @person142 @larsoner Any realistic chance this is ready and merged in ~1 week for branching? No reviewer activity in almost two years. |
@tylerjereddy I removed the milestone. |
This pull request fixes ellipj when m is near 1.0. Previously, ellipj was using equations 16.15.1-16.15.4 from Abramowitz and Stegun's Handbook of Mathematical Functions to calculate sn, cn, dn, and phi when m was near 1.0.
However, these formulas were only valid when phi < pi/2 (or equivalently when u<K, where K=ellipk(m) is the quarter-period) and so ellipj failed when u>K.
To fix this issue, this pull request will instead use formula 16.15.4 to calculate the "principal" part of the amplitude phi, or the remainder when dphi=phi/(pi/2). The total phi will be set to dphi+n*pi/2 where n=floor(u/K) (basically the number of quarter periods in u) and this total phi will be used to correctly compute sn=sin(phi) and cn=cos(phi). With this fix, ellipj will produce the right values for all u, when m is very near 1.0.
Closes #8480