By Claude:
jacobi_sn returns wrong digits for moderately large arguments, with floats and with bigfloats:
(%i1) display2d:false$
(%i2) jacobi_sn(20.0, 2.5);
(%o2) 0.2282499166210549
(%i3) jacobi_sn(20.0*%i, 0.75);
(%o3) -0.2325375293373911*%i
(%i4) jacobi_sn(20.0*%i, 1.75+%i);
(%o4) 0.827993704767369-0.8735275803124387*%i
The correct values are 0.2282498061811287, -0.2325715776527406*%i and 0.8280244930780284-0.8738216619745627*%i (computed with mpmath, and Maxima agrees at fpprec : 90). So the first result is wrong from the 7th digit on, and the others from the 4th or 5th. sin(jacobi_am(z, m)) gives the same wrong values, and jacobi_dn and jacobi_cn are affected too, for example jacobi_dn(20.0*%i, 0.75) gives 1.0200761132938823 instead of 1.020081934968419. With bigfloats at fpprec : 32, jacobi_sn(20b0*%i, 1.75b0+%i) has only about 20 correct digits. The error grows quickly with abs(z).
It's been a long time since I looked at these algorithms. Not sure where the loss of precision comes from.
jacobi_am needs lots of work. It was just a start that I never really finished. I had dome some work a few years ago to improve it and also handle complex values, but I didn't finish. All the algorithms I found had some deficiencies and I haven't worked on it since.
See the attached file for Claude's proposal for a fix.
That looks good, but I haven't tested it yet. Still has a problem when m is near 1, but I guess there's not much we can do about that.
Assuming I implemented the suggested changes correctly,
jacobi_sn(20.0,2.5)is still a bit inaccurate: 0.2282498060629115. To get the correct answer you show, I need to transform m to 1/m. Then I get the right answer.In addition, these tests fail now. The accuracy has been reduced.
Note that these tests are rather circular.
jacobi_am(z,m)usesasin(jacobi_sn(z,m))to compute the value for complex args. The differences come from computingsin(asin(z)). Perhaps we need some work on the evaluation of the trig functions and inverses for complex values.Last edit: Raymond Toy 1 day ago
Claude thinks you didn't apply all of the changes, that would explain your results.
It's currently devising a very precise version that is "essentially optimal". I will post it here when it's ready.
I saw you made a commit. Maybe you want to compare the results with what Claude proposes below (see also the attachment).
By Claude:
Thanks for testing.
0.2282498060629115is what you get with the newroot-muformula but the old stopping test(< (abs m) (epsilon u))inELLIPTIC-SN-DESCENDING(SBCL gives0.2282498060629152for that combination), so I think that hunk didn't make it into your tree. For this argument the recursion ends withimagpart(v)near 14, and stopping atabs(m) < epsdrops a term of relative size aboutm*exp(2*imagpart(v))/16, here2e-10.You're right about 1/m, though. For real
m > 1the first descending step givesabs(mu) = 1and turns a real argument into a complex one,v = z*(1+%i*sqrt(m-1))/2. With A&S 16.11 everything stays real. Atz = 20both ways give about 14 digits, because the rounding error ofvgets multiplied byz*cn*dn/sn, about 80 here. But 1/m is more accurate on average, andjacobi_dn(20.0, 2.5)no longer comes out complex.There is something we can do about
mnear 1. Two things lose digits there. At the second Landen step1-mucancels, becausemuis about1-4*sqrt(1-m). Carryingk = sqrt(1-m)through the recursion avoids that, sincesqrt(1-mu) = 2*sqrt(k)/(1+k). AndCNused the ascending transformation, wheremurounds to 1 anddn^2-sqrt(mu1)cancels. For examplejacobi_cn(20.0, 0.99999999)gives-0.2303instead of-0.5554. Getting cn and dn from the same descending recursion (A&S 16.12.3 and 16.12.4) fixes that.The attached handover replaces the earlier one completely and applies to a clean master, so please start from master if parts of the old patch are still in your tree.
ELLIPTIC-SN-DESCENDINGgets an optional argument to return cn and dn as well.SNis unchanged,CNuses it instead of the ascending transformation, andDNkeeps the Gauss transformation for real arguments withm < 8/9and uses it otherwise. Over 200 random real and complex arguments, all errors are now within 10 times eps times the condition number, with doubles and with bigfloats. It also fixes #4300. With real arguments,jacobi_sntakes at most 9% longer than before, andjacobi_dnwithm < 8/9at most 13%. Complexjacobi_sntakes up to 25% longer.jacobi_cn, andjacobi_dnin the other cases, take up to 1.4 times as long with doubles and up to 1.9 times with bigfloats, because the bottom of the recursion now needscosas well assin. I also took thelogout of the stopping test. It made bigfloat evaluation up to 10 times slower.I did apply the new condition replacing the old
|m| < eps. Without that,jacobi_sn(1+100*%i, 1e-5)is way off, as mentioned below.The new elliptic-sn-descending should use keyword args. I also dislike returning different number of values based on some parameter. It might be better to have elliptic-cn-descending and elliptic-dn-descending. Common code could be factored out. I haven't looked at that yet. (Or maybe made into a macro.)
All the comments I had previously made are gone, so it's harder to follow things. (The original was missing comments too. It's a pain to look up A&S references.)
For the example
jacobi_sn(20,2.5), it looks like we should apply the transformation http://functions.wolfram.com/09.36.16.0024.01 first to get the modulus into the range[-1,1]:Or
Then
The LHS if very close to the expected
0.2282498061811287.