From 396fc72492c09cd092abb678a078bd4a097c5c99 Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Thu, 1 Mar 2018 14:14:48 +0000 Subject: [PATCH 01/18] Add ellipj implementation --- src/elliptic.jl | 157 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 src/elliptic.jl diff --git a/src/elliptic.jl b/src/elliptic.jl new file mode 100644 index 00000000..c4c64d6f --- /dev/null +++ b/src/elliptic.jl @@ -0,0 +1,157 @@ +# References: +# [1] Abramowitz, Stegun: Handbook of Mathematical Functions (1965) +# [2] ellipjc.m in Toby Driscoll's Schwarz-Christoffel Toolbox + + +#------------------------------------------------ +# Descending and ascending Landen Transformation + +descstep(m) = m/(1+sqrt(1-m))^2 +ascstep(m) = 4*sqrt(m)/(1+sqrt(m))^2 + +@generated function shrinkm(m,::Val{N}) where {N} + # [1, Sec 16.12] + quote + f = one(m) + Base.Cartesian.@nexprs $N i->begin + k_i = descstep(m) + m = k_i^2 + f *= 1+k_i + end + return (Base.Cartesian.@ntuple $N k), f, m + end +end +@generated function growm(m,::Val{N}) where {N} + # [1, Sec 16.14] + quote + f = one(m) + Base.Cartesian.@nexprs $N i->begin + k_i = (1-sqrt(m))/(1+sqrt(m)) + m = ascstep(m) + f *= 1+k_i + end + return (Base.Cartesian.@ntuple $N k), f, m + end +end + +function ellipj_smallm(u,m) + # [1, Sec 16.13] + sn = sin(u) - m*(u-sin(u)*cos(u))*cos(u)/4 + cn = cos(u) + m*(u-sin(u)*cos(u))*sin(u)/4 + dn = 1 - m*sin(u)^2/2; + return sn,cn,dn +end +function ellipj_largem(u,m) + # [1, Sec 16.15] + sn = tanh(u) + (1-m)*(sinh(u)*cosh(u)-u)*sech(u)^2/4 + cn = sech(u) - (1-m)*(sinh(u)*cosh(u)-u)*tanh(u)*sech(u)/4 + dn = sech(u) + (1-m)*(sinh(u)*cosh(u)-u)*tanh(u)*sech(u)/4 + return sn,cn,dn +end + +@generated function ellipj_growm(sn,cn,dn, k::NTuple{N,<:Any}) where {N} + # [1, Sec 16.12] + quote + Base.Cartesian.@nexprs $N i->begin + kk = k[end-i+1] + sn,cn,dn = (1+kk)*sn/(1+kk*sn^2), + cn*dn/(1+kk*sn^2), + (1-kk*sn^2)/(1+kk*sn^2) # Use [1, 16.9.1]. Idea taken from [2] + end + return sn,cn,dn + end +end +@generated function ellipj_shrinkm(sn,cn,dn, k::NTuple{N,<:Any}) where {N} + # [1, Sec 16.14] + quote + Base.Cartesian.@nexprs $N i->begin + kk = k[end-i+1] + sn,cn,dn = (1+kk)*sn*cn/dn, + (cn^2-kk*sn^2)/dn, # Use [1, 16.9.1] + (cn^2+kk*sn^2)/dn # Use [1, 16.9.1] + end + return sn,cn,dn + end +end + +function ellipj_viasmallm(u,m,::Val{N}) where {N} + k,f,m = shrinkm(m,Val{N}()) + sn,cn,dn = ellipj_smallm(u/f,m) + return ellipj_growm(sn,cn,dn,k) +end +function ellipj_vialargem(u,m,::Val{N}) where {N} + k,f,m = growm(m,Val{N}()) + sn,cn,dn = ellipj_largem(u/f,m) + return ellipj_shrinkm(sn,cn,dn,k) +end + + +#---------------- +# Pick algorithm + +Base.@pure function ndescsteps(m,ε) + i = 0 + while abs(m) > ε + m = descstep(m)^2 + i += 1 + end + return i +end +Base.@pure function nascsteps(m,ε) + i = 0 + while abs(1-m) > ε + m = ascstep(m) + i += 1 + end + return i +end + +@generated function ellipj_dispatch(u,m) + ε = sqrt(eps(real(m))) + ndesc = ndescsteps(one(ε)/2,ε) + nasc = nascsteps(one(ε)/2,ε) + rdesc = ε + for i = 1:ndesc; rdesc = ascstep(rdesc); end + rasc = 1-ε + for i = 1:nasc ; rasc = descstep(rasc)^2; end; + rasc = 1-rasc + quote + if abs(m) < $rdesc + return ellipj_viasmallm(u,m, Val{$ndesc}()) + elseif abs(1-m) < $rasc + return ellipj_vialargem(u,m, Val{$nasc}()) + elseif imag(m) == 0 && real(m) < 0 + # [1, Sec 16.10] + sn,cn,dn = ellipj_typed(u*sqrt(1-m),-m/(1-m)) + return sn/(dn*sqrt(1-m)), cn/dn, 1/dn + else + # [1, Sec 16.11] + sn,cn,dn = ellipj_typed(u*sqrt(m),1/m) + return sn/sqrt(m), dn, cn + end + end +end + + +#----------------------------------- +# Type promotion and special values + +function ellipj_check(u,m) + if isfinite(u) && isfinite(m) + return ellipj_dispatch(u,m) + else + T = promote_type(typeof(u),typeof(m)) + return (T(NaN),T(NaN),T(NaN)) + end +end + +ellipj(u::Real,m::Real) = ellipj_check(promote(float(u),float(m))...) +ellipj(u::Complex,m::Complex) = ellipj_check(promote(float(u),float(m))...) +function ellipj(u::Complex,m::Real) + T = promote_type(float.(real.(typeof.((u,m))))...) + return ellipj_check(convert(Complex{T},u), convert(T,m)) +end +function ellipj(u::Real,m::Complex) + T = promote_type(float.(real.(typeof.((u,m))))...) + return ellipj_check(convert(T,u), convert(Complex{T},m)) +end From 863bd9ba513b2b2e35e599e59271017d4c4bcd95 Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Sat, 3 Mar 2018 11:30:05 +0000 Subject: [PATCH 02/18] Pre-cleanup --- src/elliptic.jl | 67 +++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/src/elliptic.jl b/src/elliptic.jl index c4c64d6f..694e1f1a 100644 --- a/src/elliptic.jl +++ b/src/elliptic.jl @@ -6,8 +6,10 @@ #------------------------------------------------ # Descending and ascending Landen Transformation +contracting_sqrt(m) = abs(1-m) > eps(real(typeof(m))) ? sqrt(m) : one(m) + descstep(m) = m/(1+sqrt(1-m))^2 -ascstep(m) = 4*sqrt(m)/(1+sqrt(m))^2 +ascstep(m) = 4*contracting_sqrt(m)/(1+contracting_sqrt(m))^2 @generated function shrinkm(m,::Val{N}) where {N} # [1, Sec 16.12] @@ -26,7 +28,7 @@ end quote f = one(m) Base.Cartesian.@nexprs $N i->begin - k_i = (1-sqrt(m))/(1+sqrt(m)) + k_i = (1-contracting_sqrt(m))/(1+contracting_sqrt(m)) m = ascstep(m) f *= 1+k_i end @@ -41,11 +43,11 @@ function ellipj_smallm(u,m) dn = 1 - m*sin(u)^2/2; return sn,cn,dn end -function ellipj_largem(u,m) +function ellipj_largem(u,m1) # [1, Sec 16.15] - sn = tanh(u) + (1-m)*(sinh(u)*cosh(u)-u)*sech(u)^2/4 - cn = sech(u) - (1-m)*(sinh(u)*cosh(u)-u)*tanh(u)*sech(u)/4 - dn = sech(u) + (1-m)*(sinh(u)*cosh(u)-u)*tanh(u)*sech(u)/4 + sn = tanh(u) + m1*(sinh(u)*cosh(u)-u)*sech(u)^2/4 + cn = sech(u) - m1*(sinh(u)*cosh(u)-u)*tanh(u)*sech(u)/4 + dn = sech(u) + m1*(sinh(u)*cosh(u)+u)*tanh(u)*sech(u)/4 return sn,cn,dn end @@ -56,7 +58,8 @@ end kk = k[end-i+1] sn,cn,dn = (1+kk)*sn/(1+kk*sn^2), cn*dn/(1+kk*sn^2), - (1-kk*sn^2)/(1+kk*sn^2) # Use [1, 16.9.1]. Idea taken from [2] + (1-kk*sn^2)/(1+kk*sn^2) + # ^ Use [1, 16.9.1]. Idea taken from [2] end return sn,cn,dn end @@ -80,16 +83,19 @@ function ellipj_viasmallm(u,m,::Val{N}) where {N} return ellipj_growm(sn,cn,dn,k) end function ellipj_vialargem(u,m,::Val{N}) where {N} - k,f,m = growm(m,Val{N}()) - sn,cn,dn = ellipj_largem(u/f,m) + k,f,m1 = shrinkm(1-m,Val{N}()) + sn,cn,dn = ellipj_largem(u/f,m1) return ellipj_shrinkm(sn,cn,dn,k) + # k,f,m = growm(m,Val{N}()) + # sn,cn,dn = ellipj_largem(u/f,m) + # return ellipj_shrinkm(sn,cn,dn,k) end #---------------- -# Pick algorithm +# Pick algorithm -Base.@pure function ndescsteps(m,ε) +Base.@pure function nsteps(m,ε) i = 0 while abs(m) > ε m = descstep(m)^2 @@ -97,36 +103,25 @@ Base.@pure function ndescsteps(m,ε) end return i end -Base.@pure function nascsteps(m,ε) - i = 0 - while abs(1-m) > ε - m = ascstep(m) - i += 1 - end - return i -end - @generated function ellipj_dispatch(u,m) - ε = sqrt(eps(real(m))) - ndesc = ndescsteps(one(ε)/2,ε) - nasc = nascsteps(one(ε)/2,ε) - rdesc = ε - for i = 1:ndesc; rdesc = ascstep(rdesc); end - rasc = 1-ε - for i = 1:nasc ; rasc = descstep(rasc)^2; end; - rasc = 1-rasc + T = real(m) + ε = sqrt(eps(T)) + N = max( + nsteps(one(T)/2,ε), + nsteps(sqrt(Complex{T}(0,1)),ε) + ) quote - if abs(m) < $rdesc - return ellipj_viasmallm(u,m, Val{$ndesc}()) - elseif abs(1-m) < $rasc - return ellipj_vialargem(u,m, Val{$nasc}()) + if abs(m) <= 1 && real(m) <= 0.5 + return ellipj_viasmallm(u,m, Val{$N}()) + elseif abs(1-m) <= 1 + return ellipj_vialargem(u,m, Val{$N}()) elseif imag(m) == 0 && real(m) < 0 # [1, Sec 16.10] - sn,cn,dn = ellipj_typed(u*sqrt(1-m),-m/(1-m)) + sn,cn,dn = ellipj_dispatch(u*sqrt(1-m),-m/(1-m)) return sn/(dn*sqrt(1-m)), cn/dn, 1/dn else # [1, Sec 16.11] - sn,cn,dn = ellipj_typed(u*sqrt(m),1/m) + sn,cn,dn = ellipj_dispatch(u*sqrt(m),1/m) return sn/sqrt(m), dn, cn end end @@ -148,10 +143,10 @@ end ellipj(u::Real,m::Real) = ellipj_check(promote(float(u),float(m))...) ellipj(u::Complex,m::Complex) = ellipj_check(promote(float(u),float(m))...) function ellipj(u::Complex,m::Real) - T = promote_type(float.(real.(typeof.((u,m))))...) + T = promote_type(real.(typeof.(float.((u,m))))...) return ellipj_check(convert(Complex{T},u), convert(T,m)) end function ellipj(u::Real,m::Complex) - T = promote_type(float.(real.(typeof.((u,m))))...) + T = promote_type(real.(typeof.(float.((u,m))))...) return ellipj_check(convert(T,u), convert(Complex{T},m)) end From 68fff8aa7f42a23944ffd26e20dd568b08e7af82 Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Sat, 3 Mar 2018 11:43:11 +0000 Subject: [PATCH 03/18] Post cleanup --- src/elliptic.jl | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/elliptic.jl b/src/elliptic.jl index 694e1f1a..30d70433 100644 --- a/src/elliptic.jl +++ b/src/elliptic.jl @@ -6,10 +6,8 @@ #------------------------------------------------ # Descending and ascending Landen Transformation -contracting_sqrt(m) = abs(1-m) > eps(real(typeof(m))) ? sqrt(m) : one(m) descstep(m) = m/(1+sqrt(1-m))^2 -ascstep(m) = 4*contracting_sqrt(m)/(1+contracting_sqrt(m))^2 @generated function shrinkm(m,::Val{N}) where {N} # [1, Sec 16.12] @@ -23,18 +21,6 @@ ascstep(m) = 4*contracting_sqrt(m)/(1+contracting_sqrt(m))^2 return (Base.Cartesian.@ntuple $N k), f, m end end -@generated function growm(m,::Val{N}) where {N} - # [1, Sec 16.14] - quote - f = one(m) - Base.Cartesian.@nexprs $N i->begin - k_i = (1-contracting_sqrt(m))/(1+contracting_sqrt(m)) - m = ascstep(m) - f *= 1+k_i - end - return (Base.Cartesian.@ntuple $N k), f, m - end -end function ellipj_smallm(u,m) # [1, Sec 16.13] @@ -86,9 +72,6 @@ function ellipj_vialargem(u,m,::Val{N}) where {N} k,f,m1 = shrinkm(1-m,Val{N}()) sn,cn,dn = ellipj_largem(u/f,m1) return ellipj_shrinkm(sn,cn,dn,k) - # k,f,m = growm(m,Val{N}()) - # sn,cn,dn = ellipj_largem(u/f,m) - # return ellipj_shrinkm(sn,cn,dn,k) end From 36f22628beb78f6d5ac02ba9ad4731453fabe4d9 Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Sat, 3 Mar 2018 13:39:34 +0000 Subject: [PATCH 04/18] Finish Jacobi elliptic functions --- src/elliptic.jl | 49 +++++++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/src/elliptic.jl b/src/elliptic.jl index 30d70433..2875db43 100644 --- a/src/elliptic.jl +++ b/src/elliptic.jl @@ -4,8 +4,7 @@ #------------------------------------------------ -# Descending and ascending Landen Transformation - +# Descending and Ascending Landen Transformation descstep(m) = m/(1+sqrt(1-m))^2 @@ -78,6 +77,7 @@ end #---------------- # Pick algorithm +Base.@pure puresqrt(x) = sqrt(x) Base.@pure function nsteps(m,ε) i = 0 while abs(m) > ε @@ -86,27 +86,24 @@ Base.@pure function nsteps(m,ε) end return i end -@generated function ellipj_dispatch(u,m) - T = real(m) - ε = sqrt(eps(T)) - N = max( - nsteps(one(T)/2,ε), - nsteps(sqrt(Complex{T}(0,1)),ε) - ) - quote - if abs(m) <= 1 && real(m) <= 0.5 - return ellipj_viasmallm(u,m, Val{$N}()) - elseif abs(1-m) <= 1 - return ellipj_vialargem(u,m, Val{$N}()) - elseif imag(m) == 0 && real(m) < 0 - # [1, Sec 16.10] - sn,cn,dn = ellipj_dispatch(u*sqrt(1-m),-m/(1-m)) - return sn/(dn*sqrt(1-m)), cn/dn, 1/dn - else - # [1, Sec 16.11] - sn,cn,dn = ellipj_dispatch(u*sqrt(m),1/m) - return sn/sqrt(m), dn, cn - end +Base.@pure nsteps(ε,::Type{<:Real}) = nsteps(0.5,ε) # Guarantees convergence in [-1,0.5] +Base.@pure nsteps(ε,::Type{<:Complex}) = nsteps(0.5+sqrt(3)/2im,ε) # This is heuristic. +function ellipj_dispatch(u,m) + T = promote_type(typeof(u),typeof(m)) + ε = puresqrt(eps(real(typeof(m)))) + N = nsteps(ε,typeof(m)) + if abs(m) <= 1 && real(m) <= 0.5 + return ellipj_viasmallm(u,m, Val{N}())::NTuple{3,T} + elseif abs(1-m) <= 1 + return ellipj_vialargem(u,m, Val{N}())::NTuple{3,T} + elseif imag(m) == 0 && real(m) < 0 + # [1, Sec 16.10] + sn,cn,dn = ellipj_dispatch(u*sqrt(1-m),-m/(1-m)) + return sn/(dn*sqrt(1-m)), cn/dn, 1/dn + else + # [1, Sec 16.11] + sn,cn,dn = ellipj_dispatch(u*sqrt(m),1/m) + return sn/sqrt(m), dn, cn end end @@ -124,12 +121,8 @@ function ellipj_check(u,m) end ellipj(u::Real,m::Real) = ellipj_check(promote(float(u),float(m))...) -ellipj(u::Complex,m::Complex) = ellipj_check(promote(float(u),float(m))...) function ellipj(u::Complex,m::Real) T = promote_type(real.(typeof.(float.((u,m))))...) return ellipj_check(convert(Complex{T},u), convert(T,m)) end -function ellipj(u::Real,m::Complex) - T = promote_type(real.(typeof.(float.((u,m))))...) - return ellipj_check(convert(T,u), convert(Complex{T},m)) -end +ellipj(u,m::Complex) = ellipj_check(promote(float(u),float(m))...) From 8f22ff7bfbc9444505e96dade4e385b66fb6e73e Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Sat, 3 Mar 2018 16:20:57 +0000 Subject: [PATCH 05/18] Add jpq functions --- src/elliptic.jl | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/elliptic.jl b/src/elliptic.jl index 2875db43..67bfa23b 100644 --- a/src/elliptic.jl +++ b/src/elliptic.jl @@ -126,3 +126,29 @@ function ellipj(u::Complex,m::Real) return ellipj_check(convert(Complex{T},u), convert(T,m)) end ellipj(u,m::Complex) = ellipj_check(promote(float(u),float(m))...) + + +#----------------------- +# Convenience functions + +chars = ("s","c","d") +for (i,p) in enumerate(chars) + pn = Symbol("j"*p*"n") + np = Symbol("jn"*p) + @eval begin + $pn(u,m) = ellipj(u,m)[$i] + $np(u,m) = 1/$pn(u,m) + end +end +for p in (chars...,"n") + pp = Symbol("j"*p*p) + @eval $pp(u,m) = one(promote_type(typeof.(float.((u,m)))...)) +end + +for p in chars, q in chars + p == q && continue + pq = Symbol("j"*p*q) + pn = Symbol("j"*p*"n") + qn = Symbol("j"*q*"n") + @eval $pq(u::Number,m::Number) = $pn(u,m)/$qn(u,m) +end From 3825f65521f5e601fd6dde09be312fc6815e50f0 Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Sat, 3 Mar 2018 16:21:46 +0000 Subject: [PATCH 06/18] Include in SpecialFunctions and add tests --- src/SpecialFunctions.jl | 3 ++ test/elliptic.jl | 107 ++++++++++++++++++++++++++++++++++++ test/elliptic/ellipj.bin | Bin 0 -> 361628 bytes test/elliptic/elliptic.py | 111 ++++++++++++++++++++++++++++++++++++++ test/elliptic/io.bin | Bin 0 -> 434 bytes test/runtests.jl | 2 + 6 files changed, 223 insertions(+) create mode 100644 test/elliptic.jl create mode 100644 test/elliptic/ellipj.bin create mode 100644 test/elliptic/elliptic.py create mode 100644 test/elliptic/io.bin diff --git a/src/SpecialFunctions.jl b/src/SpecialFunctions.jl index 5994a194..720479ae 100644 --- a/src/SpecialFunctions.jl +++ b/src/SpecialFunctions.jl @@ -71,7 +71,10 @@ end export sinint, cosint +export ellipj, jss,jsc,jsd,jsn,jcs,jcc,jcd,jcn,jds,jdc,jdd,jdn,jns,jnc,jnd,jnn + include("bessel.jl") +include("elliptic.jl") include("erf.jl") include("sincosint.jl") include("gamma.jl") diff --git a/test/elliptic.jl b/test/elliptic.jl new file mode 100644 index 00000000..863c847c --- /dev/null +++ b/test/elliptic.jl @@ -0,0 +1,107 @@ +@testset "elliptic" begin + +setprecision(BigFloat,256) + +function Base.read(s::IO, ::Type{BigFloat}) + setprecision(BigFloat, 256) do + x = BigFloat() + x.sign = Cint(read(s,Int8)) + unsafe_read(s, reinterpret(Ptr{UInt8},x.d), 32) + x.exp = Clong(ntoh(read(s,Int64))) + + if x.sign == 0 + return big(0) + elseif x.sign == 2 + return big(-Inf) + elseif x.sign == 3 + return big(Inf) + elseif x.sign == 4 + return big(NaN) + else + @assert unsafe_load(reinterpret(Ptr{UInt8},x.d), 32) >= 128 + # ^ MPFR crashes if the first bit in the mantissa is not set + return x + end + end +end +Base.read(s::IO, ::Type{Complex{BigFloat}}) = + complex(read(s,BigFloat),read(s,BigFloat)) + +@testset "io" begin + open("elliptic/io.bin","r") do f + @test ntoh(read(f,Int)) == 0 + @test ntoh(read(f,Int)) == 1 + @test ntoh(read(f,Int)) == -1 + @test read(f,BigFloat) == 0 + @test read(f,BigFloat) == 1 + @test read(f,BigFloat) == 2 + @test read(f,BigFloat) == -1 + @test read(f,Complex{BigFloat}) == im + @test read(f,BigFloat) == sqrt(big(2)) + @test read(f,BigFloat) == Inf + @test read(f,BigFloat) == -Inf + @test isnan(read(f,BigFloat)) + end +end + +@testset "ellipj" begin + + @testset "type stability" begin + realtypes = (Int,Float32,Float64,BigFloat) + types = (realtypes..., complex.(realtypes)...) + @testset "typeof(u) = $U, typeof(m) = $M" for U in types, M in types + @inferred ellipj(zero(U),zero(M)) + end + end + + @testset "special values" begin + vals = (0.0,Inf,NaN) + @testset "u = $u, m = $m" for u in vals, m in vals + if !isfinite(u) || !isfinite(m) + @test all(isnan.(ellipj(u,m))) + end + end + end + + @testset "mpmath" begin + open("elliptic/ellipj.bin","r") do f + npts = ntoh(read(f,Int64)) + for i = 1:npts + u = read(f, Complex{BigFloat}) + m = read(f, Complex{BigFloat}) + snref = read(f, Complex{BigFloat}) + cnref = read(f, Complex{BigFloat}) + dnref = read(f, Complex{BigFloat}) + sn,cn,dn = ellipj(u,m) + atol = eps(BigFloat)*maximum(abs.((snref,cnref,dnref))) + @test sn ≈ snref atol=atol + @test cn ≈ cnref atol=atol + @test dn ≈ dnref atol=atol + end + end + end + + @testset "precision ($T)" for T in (Float16,Float32,Float64) + n = 16 + s = @. exp(2T(π)*im*(0:n-1)/n) + r = [eps(T), π*eps(T), sqrt(eps(T)), π*sqrt(eps(T)), 1/e, 0.5, e/π, 1] + x = [0; vec(s.*r')] + for u = x + for m0 = (0,1) + for m = m0 .+ x + sn,cn,dn = ellipj(u,m) + snref,cnref,dnref = ellipj(big(u),big(m)) + + rtol = 0 + atol = 10*eps(T)*maximum(abs.((snref,cnref,dnref))) + @test sn ≈ snref rtol=rtol atol = atol + @test cn ≈ cnref rtol=rtol atol = atol + @test dn ≈ dnref rtol=rtol atol = atol + end + end + end + end + +end + +end diff --git a/test/elliptic/ellipj.bin b/test/elliptic/ellipj.bin new file mode 100644 index 0000000000000000000000000000000000000000..2313602fa5c03fdb64cd7090f75756c49dfcccca GIT binary patch literal 361628 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 literal 0 HcmV?d00001 diff --git a/test/elliptic/elliptic.py b/test/elliptic/elliptic.py new file mode 100644 index 00000000..51077e2c --- /dev/null +++ b/test/elliptic/elliptic.py @@ -0,0 +1,111 @@ +# Create test set for elliptic functions + +import mpmath + +mpmath.mp.prec = 256 + +def isplusinf(x): return x == mpmath.mp.inf or (x > 0 and x.exp + x.bc >= 2**63) +def isminusinf(x): return isplusinf(-x) +def isinf(x): return isplusinf(x) or isminusinf(x) + +def parse_int8(x): + return x.to_bytes(1, "big", signed=True) + +def parse_int64(x): + return x.to_bytes(8, "big", signed=True) + +def reverse_bits(x,nbits): + y = 0 + for i in range(nbits): + y <<= 1 + y += (x&1) + x >>= 1 + return y + +def parse_mantissa(x): + if x == 0: + return bytes(32) + m = reverse_bits(x.man, x.man.bit_length()) + b = m.to_bytes(32,"big",signed=False) + return bytes(map(lambda b: reverse_bits(b,8), b)) + +def parse_exponent(x): + return parse_int64(x.exp+x.bc) + +def parse_mpf(x): + s = bytes() + if x < 0 and not isminusinf(x): + s = parse_int8(-1) + if x == 0: + s = parse_int8(0) + if x > 0 and not isplusinf(x): + s = parse_int8(1) + if isminusinf(x): + s = parse_int8(2) + x = mpmath.mpf(0) + if isplusinf(x): + s = parse_int8(3) + x = mpmath.mpf(0) + if mpmath.isnan(x): + s = parse_int8(4) + x = mpmath.mpf(0) + return s + parse_mantissa(x) + parse_exponent(x) + +def parse_mpc(x): + return parse_mpf(x.real) + parse_mpf(x.imag) + +def dump_testset(name,u,m): + with open(name,"wb") as f: + f.write(parse_int64(len(u)*len(m))) + for ui in u: + for mi in m: + sn,cn,dn = ellipj(ui,mi) + f.write(parse_mpc(ui)) + f.write(parse_mpc(mi)) + f.write(parse_mpc(sn)) + f.write(parse_mpc(cn)) + f.write(parse_mpc(dn)) + + + +def ellipj(u,m): + sn = mpmath.ellipfun("sn",u,m=m) + cn = mpmath.ellipfun("cn",u,m=m) + dn = mpmath.ellipfun("dn",u,m=m) + if u == 0: sn = 0*sn # https://github.com/fredrik-johansson/mpmath/issues/386 + return sn,cn,dn + + +def test_io(): + with open("io.bin","wb") as f: + f.write(parse_int64(0)) + f.write(parse_int64(1)) + f.write(parse_int64(-1)) + f.write(parse_mpf(mpmath.mpf( 0))) + f.write(parse_mpf(mpmath.mpf( 1))) + f.write(parse_mpf(mpmath.mpf( 2))) + f.write(parse_mpf(mpmath.mpf(-1))) + f.write(parse_mpc(mpmath.mpc(1j))) + f.write(parse_mpf(mpmath.sqrt(mpmath.mpf(2)))) + f.write(parse_mpf(mpmath.mp.inf)) + f.write(parse_mpf(-mpmath.mp.inf)) + f.write(parse_mpf(mpmath.mp.nan)) +test_io() + +def test_ellipj(): + s = [ + mpmath.mpc(1,0), + mpmath.sqrt(mpmath.mpc(0,1)), + mpmath.mpc(0,1), + mpmath.mpc(-1,0), + ] + r = [ + mpmath.mpf(mpmath.mp.eps), + mpmath.sqrt(mpmath.mp.eps), + 1/mpmath.mp.e, + mpmath.mpf("1"), + mpmath.mp.e, + ] + x = [mpmath.mpc(0)] + [si*ri for si in s for ri in r] + dump_testset("ellipj.bin",x, x+[1+mi for mi in x]) +test_ellipj() diff --git a/test/elliptic/io.bin b/test/elliptic/io.bin new file mode 100644 index 0000000000000000000000000000000000000000..595697ef5a79b180e7f08ac5c99f7163d772b25b GIT binary patch literal 434 zcmZQzKmm;Zp#Vh`ABPd2Bv7saD$hu)LZ<)3nn*w$LYi2s85u=q8aKDD5r6Bm!n$); pLcZ+$eO{%TJ8L6bQttgU{>-u!<{4&$;rLlh_@xoDEC?ok762|g8qWX# literal 0 HcmV?d00001 diff --git a/test/runtests.jl b/test/runtests.jl index c82a4cfa..a361b0e9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -558,3 +558,5 @@ end end @test sprint(showerror, SF.AmosException(1)) == "AmosException with id 1: input error." + +include("elliptic.jl") From 2660d168d78518dbf02f85c3a9c3275c62972d14 Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Mon, 5 Mar 2018 17:52:23 +0000 Subject: [PATCH 07/18] Update --- src/SpecialFunctions.jl | 4 +- src/elliptic.jl | 88 ++++++++++++++++++++++++++++++++++++-- test/elliptic.jl | 43 ++++++++++++++++++- test/elliptic/K.bin | Bin 0 -> 47568 bytes test/elliptic/elliptic.py | 67 +++++++++++++++++++++-------- 5 files changed, 177 insertions(+), 25 deletions(-) create mode 100644 test/elliptic/K.bin diff --git a/src/SpecialFunctions.jl b/src/SpecialFunctions.jl index 720479ae..77585fe5 100644 --- a/src/SpecialFunctions.jl +++ b/src/SpecialFunctions.jl @@ -71,7 +71,9 @@ end export sinint, cosint -export ellipj, jss,jsc,jsd,jsn,jcs,jcc,jcd,jcn,jds,jdc,jdd,jdn,jns,jnc,jnd,jnn +export ellipj, + jss,jsc,jsd,jsn,jcs,jcc,jcd,jcn,jds,jdc,jdd,jdn,jns,jnc,jnd,jnn, + K, iK include("bessel.jl") include("elliptic.jl") diff --git a/src/elliptic.jl b/src/elliptic.jl index 67bfa23b..d9397d80 100644 --- a/src/elliptic.jl +++ b/src/elliptic.jl @@ -127,6 +127,17 @@ function ellipj(u::Complex,m::Real) end ellipj(u,m::Complex) = ellipj_check(promote(float(u),float(m))...) +""" + ellipj(u,m) -> sn,cn,dn + +Evaluate the Jacobi elliptic functions `sn`, `cn` and `dn`. + +Convenience function `jpq(u,m)` with `p,q ∈ {s,c,d,n}` are also +provided, but this function is more efficient if more than one elliptic +function with the same arguments is required. +""" +function ellipj end + #----------------------- # Convenience functions @@ -148,7 +159,78 @@ end for p in chars, q in chars p == q && continue pq = Symbol("j"*p*q) - pn = Symbol("j"*p*"n") - qn = Symbol("j"*q*"n") - @eval $pq(u::Number,m::Number) = $pn(u,m)/$qn(u,m) + pn = Symbol(p*"n") + qn = Symbol(q*"n") + @eval begin + function $pq(u::Number,m::Number) + sn,cn,dn = ellipj(u,m) + return $pn/$qn + end + end +end + +for p in (chars...,"n"), q in (chars...,"n") + pq = Symbol("j"*p*q) + @eval begin +""" + $(string($pq))(u,m) + +Evaluate the Jacobi elliptic function `$($p)$($q)`. + +See also `ellipj(u,m)` if more than one Jacobi elliptic function +with the same arguments is required. +""" +function $pq end + end end + + +#---------------------------------------------- +# Complete elliptic integral of the first kind + +function iK_agm(m) + # [1, Sec 17.6] + T = typeof(m) + m == 0 && return T(Inf) + isnan(m) && return T(NaN) + a,b = one(m),sqrt(m) + while abs(a-b) > 2*eps(abs(a)) + a,b = (a+b)/2,sqrt(a*b) + end + return T(π)/(2*a) # https://github.com/JuliaLang/julia/issues/26324 +end +iK(m) = iK_agm(float(m)) +K(m::Real) = iK(1-m) +function K(m::Complex) + # Make sure we hit the "right" branch of sqrt if imag(m) == 0. + # Here, "right" is defined as being consistent with mpmath. + if imag(m) == 0 + return iK(complex(1-real(m),imag(m))) + else + return iK(1-m) + end +end + +""" + iK(m1) + +Evaluate `K(1-m1)` with enhanced precision for small values of `m1`. +""" +function iK end + +doc""" + K(m) + +Evaluate the complete elliptic integral of the first kind. + +```math +\begin{aligned} + K(m) + &= \int_0^1 \big((1-t^2)\,(1-mt^2)\big)^{-1/2} \, dt \\ + &= \int_0^{π/2} (1-m \sin^2\theta)^{-1/2} \, d\theta +\end{align} +``` + +See also `iK(m)` for evaluating `K(m)` for values close to `1`. +""" +function K end diff --git a/test/elliptic.jl b/test/elliptic.jl index 863c847c..2ba5130c 100644 --- a/test/elliptic.jl +++ b/test/elliptic.jl @@ -84,8 +84,8 @@ end @testset "precision ($T)" for T in (Float16,Float32,Float64) n = 16 s = @. exp(2T(π)*im*(0:n-1)/n) - r = [eps(T), π*eps(T), sqrt(eps(T)), π*sqrt(eps(T)), 1/e, 0.5, e/π, 1] - x = [0; vec(s.*r')] + r = T[eps(T), π*eps(T), sqrt(eps(T)), π*sqrt(eps(T)), 1/e, 0.5, e/π, 1] + x = Complex{T}[0; vec(s.*r')] for u = x for m0 = (0,1) for m = m0 .+ x @@ -104,4 +104,43 @@ end end +@testset "K" begin + @testset "type stability" begin + realtypes = (Int,Float32,Float64,BigFloat) + types = (realtypes..., complex.(realtypes)...) + @testset "typeof(m) = $M" for M in types + @inferred K(zero(M)) + end + end + + @testset "special values" begin + @test isnan(K(NaN)) + @test K(Inf+0im) == 0.0 + @test K(-Inf) == 0.0 + end + + @testset "mpmath" begin + open("elliptic/K.bin","r") do f + npts = ntoh(read(f,Int64)) + for i = 1:npts + m = read(f, Complex{BigFloat}) + Kref = read(f, Complex{BigFloat}) + @test K(m) ≈ Kref + end + end + end + + @testset "precision ($T)" for T in (Float16,Float32,Float64) + n = 16 + s = @. exp(2T(π)*im*(0:n-1)/n) + r = T[eps(T), π*eps(T), sqrt(eps(T)), π*sqrt(eps(T)), 1/e, 0.5, e/π, 1, e, π, 1/sqrt(eps(T)), 1/eps(T)] + x = Complex{T}[0; vec(s.*r')] + for m0 = (0,1) + for m = m0 .+ x + @test K(m) ≈ K(big(m)) rtol=2*eps(T) + end + end + end +end + end diff --git a/test/elliptic/K.bin b/test/elliptic/K.bin new file mode 100644 index 0000000000000000000000000000000000000000..5eb25ba1b1f9f23b58a5e26add6f1be5d0f884d1 GIT binary patch literal 47568 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 Pdz`ZY`WC{VzJ>n>sc$@B literal 0 HcmV?d00001 diff --git a/test/elliptic/elliptic.py b/test/elliptic/elliptic.py index 51077e2c..7d8f2e1c 100644 --- a/test/elliptic/elliptic.py +++ b/test/elliptic/elliptic.py @@ -54,6 +54,24 @@ def parse_mpf(x): def parse_mpc(x): return parse_mpf(x.real) + parse_mpf(x.imag) + +def test_io(): + with open("io.bin","wb") as f: + f.write(parse_int64(0)) + f.write(parse_int64(1)) + f.write(parse_int64(-1)) + f.write(parse_mpf(mpmath.mpf( 0))) + f.write(parse_mpf(mpmath.mpf( 1))) + f.write(parse_mpf(mpmath.mpf( 2))) + f.write(parse_mpf(mpmath.mpf(-1))) + f.write(parse_mpc(mpmath.mpc(1j))) + f.write(parse_mpf(mpmath.sqrt(mpmath.mpf(2)))) + f.write(parse_mpf(mpmath.mp.inf)) + f.write(parse_mpf(-mpmath.mp.inf)) + f.write(parse_mpf(mpmath.mp.nan)) +test_io() + + def dump_testset(name,u,m): with open(name,"wb") as f: f.write(parse_int64(len(u)*len(m))) @@ -66,8 +84,6 @@ def dump_testset(name,u,m): f.write(parse_mpc(cn)) f.write(parse_mpc(dn)) - - def ellipj(u,m): sn = mpmath.ellipfun("sn",u,m=m) cn = mpmath.ellipfun("cn",u,m=m) @@ -75,23 +91,6 @@ def ellipj(u,m): if u == 0: sn = 0*sn # https://github.com/fredrik-johansson/mpmath/issues/386 return sn,cn,dn - -def test_io(): - with open("io.bin","wb") as f: - f.write(parse_int64(0)) - f.write(parse_int64(1)) - f.write(parse_int64(-1)) - f.write(parse_mpf(mpmath.mpf( 0))) - f.write(parse_mpf(mpmath.mpf( 1))) - f.write(parse_mpf(mpmath.mpf( 2))) - f.write(parse_mpf(mpmath.mpf(-1))) - f.write(parse_mpc(mpmath.mpc(1j))) - f.write(parse_mpf(mpmath.sqrt(mpmath.mpf(2)))) - f.write(parse_mpf(mpmath.mp.inf)) - f.write(parse_mpf(-mpmath.mp.inf)) - f.write(parse_mpf(mpmath.mp.nan)) -test_io() - def test_ellipj(): s = [ mpmath.mpc(1,0), @@ -109,3 +108,33 @@ def test_ellipj(): x = [mpmath.mpc(0)] + [si*ri for si in s for ri in r] dump_testset("ellipj.bin",x, x+[1+mi for mi in x]) test_ellipj() + + +def dump_testset(name,m): + with open(name,"wb") as f: + f.write(parse_int64(len(m))) + for mi in m: + K = mpmath.ellipk(mi) + f.write(parse_mpc(mi)) + f.write(parse_mpc(K)) + +def test_K(): + ns = 16 + s = [ + mpmath.exp(2*mpmath.mp.pi*1j*i/ns) + for i in range(ns) + ] + r = [ + mpmath.mpf(mpmath.mp.eps), + mpmath.sqrt(mpmath.mp.eps), + 1/mpmath.mp.pi, + 1/mpmath.mp.e, + mpmath.mpf("0.5"), + mpmath.mpf("1"), + mpmath.mpf("2"), + mpmath.mp.e, + mpmath.mp.pi, + ] + m = [mpmath.mpc(0)] + [si*ri for si in s for ri in r] + dump_testset("K.bin",m + [1+mi for mi in m]) +test_K() From de7d83b2c765e1dc803a6170be23eeec7c66934f Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Mon, 5 Mar 2018 18:08:37 +0000 Subject: [PATCH 08/18] Update documentation --- docs/src/index.md | 3 +++ docs/src/special.md | 2 ++ src/elliptic.jl | 12 +++++------- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index 4dead14b..d1f24de6 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -40,6 +40,9 @@ libraries. | [`besselix(nu,z)`](@ref SpecialFunctions.besselix) | scaled modified Bessel function of the first kind of order `nu` at `z` | | [`besselk(nu,z)`](@ref SpecialFunctions.besselk) | modified [Bessel function](https://en.wikipedia.org/wiki/Bessel_function) of the second kind of order `nu` at `z` | | [`besselkx(nu,z)`](@ref SpecialFunctions.besselkx) | scaled modified Bessel function of the second kind of order `nu` at `z` | +| [`ellipj(u,m)`](@ref SpecialFunctions.ellipj) | Jacobi elliptic functions `sn,cn,dn` | +| `jpq(u,m)` | Jacobi elliptic function `pq` | +| [`K(m)`](@ref SpecialFunctions.ellipj) | Complete elliptic integral of the first kind | ## Installation diff --git a/docs/src/special.md b/docs/src/special.md index f8bb48b1..43b74ddc 100644 --- a/docs/src/special.md +++ b/docs/src/special.md @@ -46,4 +46,6 @@ SpecialFunctions.besselk SpecialFunctions.besselkx SpecialFunctions.eta SpecialFunctions.zeta +SpecialFunctions.ellipj +SpecialFunctions.K ``` diff --git a/src/elliptic.jl b/src/elliptic.jl index d9397d80..cd72d576 100644 --- a/src/elliptic.jl +++ b/src/elliptic.jl @@ -130,7 +130,7 @@ ellipj(u,m::Complex) = ellipj_check(promote(float(u),float(m))...) """ ellipj(u,m) -> sn,cn,dn -Evaluate the Jacobi elliptic functions `sn`, `cn` and `dn`. +Jacobi elliptic functions `sn`, `cn` and `dn`. Convenience function `jpq(u,m)` with `p,q ∈ {s,c,d,n}` are also provided, but this function is more efficient if more than one elliptic @@ -175,7 +175,7 @@ for p in (chars...,"n"), q in (chars...,"n") """ $(string($pq))(u,m) -Evaluate the Jacobi elliptic function `$($p)$($q)`. +Jacobi elliptic function `$($p)$($q)`. See also `ellipj(u,m)` if more than one Jacobi elliptic function with the same arguments is required. @@ -214,23 +214,21 @@ end """ iK(m1) -Evaluate `K(1-m1)` with enhanced precision for small values of `m1`. +Evaluate `K(1-m1)` with better precision for small values of `m1`. """ function iK end doc""" K(m) -Evaluate the complete elliptic integral of the first kind. +Complete elliptic integral of the first kind. ```math \begin{aligned} K(m) &= \int_0^1 \big((1-t^2)\,(1-mt^2)\big)^{-1/2} \, dt \\ &= \int_0^{π/2} (1-m \sin^2\theta)^{-1/2} \, d\theta -\end{align} +\end{aligned} ``` - -See also `iK(m)` for evaluating `K(m)` for values close to `1`. """ function K end From 6f0d23d2f981b56d1a8bd7544f1a093d593ebebf Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Mon, 5 Mar 2018 19:28:52 +0000 Subject: [PATCH 09/18] Fix 0.7 deprecations and erros. --- test/elliptic.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/elliptic.jl b/test/elliptic.jl index 2ba5130c..5397500d 100644 --- a/test/elliptic.jl +++ b/test/elliptic.jl @@ -73,7 +73,7 @@ end cnref = read(f, Complex{BigFloat}) dnref = read(f, Complex{BigFloat}) sn,cn,dn = ellipj(u,m) - atol = eps(BigFloat)*maximum(abs.((snref,cnref,dnref))) + atol = sqrt(eps(BigFloat))*maximum(abs.((snref,cnref,dnref))) @test sn ≈ snref atol=atol @test cn ≈ cnref atol=atol @test dn ≈ dnref atol=atol @@ -84,7 +84,7 @@ end @testset "precision ($T)" for T in (Float16,Float32,Float64) n = 16 s = @. exp(2T(π)*im*(0:n-1)/n) - r = T[eps(T), π*eps(T), sqrt(eps(T)), π*sqrt(eps(T)), 1/e, 0.5, e/π, 1] + r = T[eps(T), π*eps(T), sqrt(eps(T)), π*sqrt(eps(T)), 1/π, 0.5, 3/π, 1] x = Complex{T}[0; vec(s.*r')] for u = x for m0 = (0,1) @@ -133,11 +133,11 @@ end @testset "precision ($T)" for T in (Float16,Float32,Float64) n = 16 s = @. exp(2T(π)*im*(0:n-1)/n) - r = T[eps(T), π*eps(T), sqrt(eps(T)), π*sqrt(eps(T)), 1/e, 0.5, e/π, 1, e, π, 1/sqrt(eps(T)), 1/eps(T)] + r = T[eps(T), π*eps(T), sqrt(eps(T)), π*sqrt(eps(T)), 1/T(π), 0.5, 1, π, 1/sqrt(eps(T)), 1/eps(T)] x = Complex{T}[0; vec(s.*r')] for m0 = (0,1) for m = m0 .+ x - @test K(m) ≈ K(big(m)) rtol=2*eps(T) + @test K(m) ≈ K(big(m)) rtol=3*eps(T) end end end From 2d406dc82532a82037837b45a63a82a3fa09a365 Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Mon, 5 Mar 2018 20:00:41 +0000 Subject: [PATCH 10/18] Fix test error on 32bit systems (fingers crossed) --- test/elliptic.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/elliptic.jl b/test/elliptic.jl index 5397500d..4487574e 100644 --- a/test/elliptic.jl +++ b/test/elliptic.jl @@ -29,9 +29,9 @@ Base.read(s::IO, ::Type{Complex{BigFloat}}) = @testset "io" begin open("elliptic/io.bin","r") do f - @test ntoh(read(f,Int)) == 0 - @test ntoh(read(f,Int)) == 1 - @test ntoh(read(f,Int)) == -1 + @test ntoh(read(f,Int64)) == 0 + @test ntoh(read(f,Int64)) == 1 + @test ntoh(read(f,Int64)) == -1 @test read(f,BigFloat) == 0 @test read(f,BigFloat) == 1 @test read(f,BigFloat) == 2 From 9f16249f111c0c47ab3599ced5e9fbb3c520e4f5 Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Mon, 5 Mar 2018 22:28:28 +0000 Subject: [PATCH 11/18] Factor trigonometric and hyperbolic functions into variables --- src/elliptic.jl | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/elliptic.jl b/src/elliptic.jl index cd72d576..46634171 100644 --- a/src/elliptic.jl +++ b/src/elliptic.jl @@ -23,16 +23,26 @@ end function ellipj_smallm(u,m) # [1, Sec 16.13] - sn = sin(u) - m*(u-sin(u)*cos(u))*cos(u)/4 - cn = cos(u) + m*(u-sin(u)*cos(u))*sin(u)/4 - dn = 1 - m*sin(u)^2/2; + if VERSION < v"0.7-" + sinu = sin(u) + cosu = cos(u) + else + sinu, cosu = sincos(u) + end + sn = sinu - m*(u-sinu*cosu)*cosu/4 + cn = cosu + m*(u-sinu*cosu)*sinu/4 + dn = 1 - m*sinu^2/2; return sn,cn,dn end function ellipj_largem(u,m1) # [1, Sec 16.15] - sn = tanh(u) + m1*(sinh(u)*cosh(u)-u)*sech(u)^2/4 - cn = sech(u) - m1*(sinh(u)*cosh(u)-u)*tanh(u)*sech(u)/4 - dn = sech(u) + m1*(sinh(u)*cosh(u)+u)*tanh(u)*sech(u)/4 + sinhu = sinh(u) + coshu = cosh(u) + tanhu = tanh(u) + sechu = sech(u) + sn = tanhu + m1*(sinhu*coshu-u)*sechu^2/4 + cn = sechu - m1*(sinhu*coshu-u)*tanhu*sechu/4 + dn = sechu + m1*(sinhu*coshu+u)*tanhu*sechu/4 return sn,cn,dn end From dbc4bd4f258da7612d516a0c548db9beac9a993e Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Mon, 5 Mar 2018 22:28:56 +0000 Subject: [PATCH 12/18] Align equations --- src/elliptic.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/elliptic.jl b/src/elliptic.jl index 46634171..d7871360 100644 --- a/src/elliptic.jl +++ b/src/elliptic.jl @@ -52,9 +52,9 @@ end Base.Cartesian.@nexprs $N i->begin kk = k[end-i+1] sn,cn,dn = (1+kk)*sn/(1+kk*sn^2), - cn*dn/(1+kk*sn^2), - (1-kk*sn^2)/(1+kk*sn^2) - # ^ Use [1, 16.9.1]. Idea taken from [2] + cn*dn/(1+kk*sn^2), + (1-kk*sn^2)/(1+kk*sn^2) + # ^ Use [1, 16.9.1]. Idea taken from [2] end return sn,cn,dn end @@ -65,8 +65,8 @@ end Base.Cartesian.@nexprs $N i->begin kk = k[end-i+1] sn,cn,dn = (1+kk)*sn*cn/dn, - (cn^2-kk*sn^2)/dn, # Use [1, 16.9.1] - (cn^2+kk*sn^2)/dn # Use [1, 16.9.1] + (cn^2-kk*sn^2)/dn, # Use [1, 16.9.1] + (cn^2+kk*sn^2)/dn # Use [1, 16.9.1] end return sn,cn,dn end From 3b34edde0a12b7b55d510024abb4026cb08423f5 Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Mon, 5 Mar 2018 22:38:13 +0000 Subject: [PATCH 13/18] Add references to DLMF --- src/elliptic.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/elliptic.jl b/src/elliptic.jl index d7871360..de34bce0 100644 --- a/src/elliptic.jl +++ b/src/elliptic.jl @@ -9,7 +9,7 @@ descstep(m) = m/(1+sqrt(1-m))^2 @generated function shrinkm(m,::Val{N}) where {N} - # [1, Sec 16.12] + # [1, Sec 16.12] / https://dlmf.nist.gov/22.7.i quote f = one(m) Base.Cartesian.@nexprs $N i->begin @@ -22,7 +22,7 @@ descstep(m) = m/(1+sqrt(1-m))^2 end function ellipj_smallm(u,m) - # [1, Sec 16.13] + # [1, Sec 16.13] / https://dlmf.nist.gov/22.10.ii if VERSION < v"0.7-" sinu = sin(u) cosu = cos(u) @@ -35,7 +35,7 @@ function ellipj_smallm(u,m) return sn,cn,dn end function ellipj_largem(u,m1) - # [1, Sec 16.15] + # [1, Sec 16.15] / https://dlmf.nist.gov/22.10.ii sinhu = sinh(u) coshu = cosh(u) tanhu = tanh(u) @@ -47,7 +47,7 @@ function ellipj_largem(u,m1) end @generated function ellipj_growm(sn,cn,dn, k::NTuple{N,<:Any}) where {N} - # [1, Sec 16.12] + # [1, Sec 16.12] / https://dlmf.nist.gov/22.7.i quote Base.Cartesian.@nexprs $N i->begin kk = k[end-i+1] @@ -60,7 +60,7 @@ end end end @generated function ellipj_shrinkm(sn,cn,dn, k::NTuple{N,<:Any}) where {N} - # [1, Sec 16.14] + # [1, Sec 16.14] / https://dlmf.nist.gov/22.7.ii quote Base.Cartesian.@nexprs $N i->begin kk = k[end-i+1] From 47d4a5501c08f697ceb64a5a0e28bc42c5754636 Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Tue, 6 Mar 2018 10:51:47 +0000 Subject: [PATCH 14/18] Make sure `nsteps` gets called only once even for `BigFloat`s. --- src/elliptic.jl | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/elliptic.jl b/src/elliptic.jl index de34bce0..48031178 100644 --- a/src/elliptic.jl +++ b/src/elliptic.jl @@ -87,7 +87,25 @@ end #---------------- # Pick algorithm -Base.@pure puresqrt(x) = sqrt(x) +@generated function ellipj_dispatch(u,m, ::Val{N}) where {N} + quote + if abs(m) <= 1 && real(m) <= 0.5 + return ellipj_viasmallm(u,m, Val{$N}()) + elseif abs(1-m) <= 1 + return ellipj_vialargem(u,m, Val{$N}()) + elseif imag(m) == 0 && real(m) < 0 + # [1, Sec 16.10] + sn,cn,dn = ellipj_dispatch(u*sqrt(1-m),-m/(1-m), Val{$N}()) + return sn/(dn*sqrt(1-m)), cn/dn, 1/dn + else + # [1, Sec 16.11] + sn,cn,dn = ellipj_dispatch(u*sqrt(m),1/m, Val{$N}()) + return sn/sqrt(m), dn, cn + end + end +end + +Base.@pure puresqrt(x) = Float64(sqrt(x)) Base.@pure function nsteps(m,ε) i = 0 while abs(m) > ε @@ -98,23 +116,14 @@ Base.@pure function nsteps(m,ε) end Base.@pure nsteps(ε,::Type{<:Real}) = nsteps(0.5,ε) # Guarantees convergence in [-1,0.5] Base.@pure nsteps(ε,::Type{<:Complex}) = nsteps(0.5+sqrt(3)/2im,ε) # This is heuristic. -function ellipj_dispatch(u,m) +function ellipj_nsteps(u,m) + # Compute the number of Landen steps required to reach machine precision. + # For all FloatXX types, this can be done at compile time, while for + # BigFloat this has to be done at runtime. T = promote_type(typeof(u),typeof(m)) ε = puresqrt(eps(real(typeof(m)))) N = nsteps(ε,typeof(m)) - if abs(m) <= 1 && real(m) <= 0.5 - return ellipj_viasmallm(u,m, Val{N}())::NTuple{3,T} - elseif abs(1-m) <= 1 - return ellipj_vialargem(u,m, Val{N}())::NTuple{3,T} - elseif imag(m) == 0 && real(m) < 0 - # [1, Sec 16.10] - sn,cn,dn = ellipj_dispatch(u*sqrt(1-m),-m/(1-m)) - return sn/(dn*sqrt(1-m)), cn/dn, 1/dn - else - # [1, Sec 16.11] - sn,cn,dn = ellipj_dispatch(u*sqrt(m),1/m) - return sn/sqrt(m), dn, cn - end + return ellipj_dispatch(u,m,Val{N}())::NTuple{3,T} end @@ -123,7 +132,7 @@ end function ellipj_check(u,m) if isfinite(u) && isfinite(m) - return ellipj_dispatch(u,m) + return ellipj_nsteps(u,m) else T = promote_type(typeof(u),typeof(m)) return (T(NaN),T(NaN),T(NaN)) From 0eee6b24449407caa58d1ab6d5e3695d6f703e31 Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Tue, 6 Mar 2018 21:35:06 +0000 Subject: [PATCH 15/18] Remove some @generated. --- src/elliptic.jl | 62 +++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/src/elliptic.jl b/src/elliptic.jl index 48031178..946be687 100644 --- a/src/elliptic.jl +++ b/src/elliptic.jl @@ -46,30 +46,24 @@ function ellipj_largem(u,m1) return sn,cn,dn end -@generated function ellipj_growm(sn,cn,dn, k::NTuple{N,<:Any}) where {N} +function ellipj_growm(sn,cn,dn, k) # [1, Sec 16.12] / https://dlmf.nist.gov/22.7.i - quote - Base.Cartesian.@nexprs $N i->begin - kk = k[end-i+1] - sn,cn,dn = (1+kk)*sn/(1+kk*sn^2), - cn*dn/(1+kk*sn^2), - (1-kk*sn^2)/(1+kk*sn^2) - # ^ Use [1, 16.9.1]. Idea taken from [2] - end - return sn,cn,dn + for kk in reverse(k) + sn,cn,dn = (1+kk)*sn/(1+kk*sn^2), + cn*dn/(1+kk*sn^2), + (1-kk*sn^2)/(1+kk*sn^2) + # ^ Use [1, 16.9.1]. Idea taken from [2] end + return sn,cn,dn end -@generated function ellipj_shrinkm(sn,cn,dn, k::NTuple{N,<:Any}) where {N} +function ellipj_shrinkm(sn,cn,dn, k::NTuple{N,<:Any}) where {N} # [1, Sec 16.14] / https://dlmf.nist.gov/22.7.ii - quote - Base.Cartesian.@nexprs $N i->begin - kk = k[end-i+1] - sn,cn,dn = (1+kk)*sn*cn/dn, - (cn^2-kk*sn^2)/dn, # Use [1, 16.9.1] - (cn^2+kk*sn^2)/dn # Use [1, 16.9.1] - end - return sn,cn,dn + for kk in reverse(k) + sn,cn,dn = (1+kk)*sn*cn/dn, + (cn^2-kk*sn^2)/dn, # Use [1, 16.9.1] + (cn^2+kk*sn^2)/dn # Use [1, 16.9.1] end + return sn,cn,dn end function ellipj_viasmallm(u,m,::Val{N}) where {N} @@ -87,21 +81,19 @@ end #---------------- # Pick algorithm -@generated function ellipj_dispatch(u,m, ::Val{N}) where {N} - quote - if abs(m) <= 1 && real(m) <= 0.5 - return ellipj_viasmallm(u,m, Val{$N}()) - elseif abs(1-m) <= 1 - return ellipj_vialargem(u,m, Val{$N}()) - elseif imag(m) == 0 && real(m) < 0 - # [1, Sec 16.10] - sn,cn,dn = ellipj_dispatch(u*sqrt(1-m),-m/(1-m), Val{$N}()) - return sn/(dn*sqrt(1-m)), cn/dn, 1/dn - else - # [1, Sec 16.11] - sn,cn,dn = ellipj_dispatch(u*sqrt(m),1/m, Val{$N}()) - return sn/sqrt(m), dn, cn - end +function ellipj_dispatch(u,m, ::Val{N}) where {N} + if abs(m) <= 1 && real(m) <= 0.5 + return ellipj_viasmallm(u,m, Val{N}()) + elseif abs(1-m) <= 1 + return ellipj_vialargem(u,m, Val{N}()) + elseif imag(m) == 0 && real(m) < 0 + # [1, Sec 16.10] + sn,cn,dn = ellipj_dispatch(u*sqrt(1-m),-m/(1-m), Val{N}()) + return sn/(dn*sqrt(1-m)), cn/dn, 1/dn + else + # [1, Sec 16.11] + sn,cn,dn = ellipj_dispatch(u*sqrt(m),1/m, Val{N}()) + return sn/sqrt(m), dn, cn end end @@ -118,7 +110,7 @@ Base.@pure nsteps(ε,::Type{<:Real}) = nsteps(0.5,ε) # Guarantees convergence i Base.@pure nsteps(ε,::Type{<:Complex}) = nsteps(0.5+sqrt(3)/2im,ε) # This is heuristic. function ellipj_nsteps(u,m) # Compute the number of Landen steps required to reach machine precision. - # For all FloatXX types, this can be done at compile time, while for + # For all FloatXX types, this can be done at compile time, while for # BigFloat this has to be done at runtime. T = promote_type(typeof(u),typeof(m)) ε = puresqrt(eps(real(typeof(m)))) From f4f00b9617276b1507686251734c362bb7dd8dc9 Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Tue, 6 Mar 2018 22:46:55 +0000 Subject: [PATCH 16/18] Optimise evaluation of tanh(u) and sech(u) --- src/elliptic.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/elliptic.jl b/src/elliptic.jl index 946be687..e6621d27 100644 --- a/src/elliptic.jl +++ b/src/elliptic.jl @@ -38,8 +38,8 @@ function ellipj_largem(u,m1) # [1, Sec 16.15] / https://dlmf.nist.gov/22.10.ii sinhu = sinh(u) coshu = cosh(u) - tanhu = tanh(u) - sechu = sech(u) + tanhu = sinhu/coshu + sechu = 1/coshu sn = tanhu + m1*(sinhu*coshu-u)*sechu^2/4 cn = sechu - m1*(sinhu*coshu-u)*tanhu*sechu/4 dn = sechu + m1*(sinhu*coshu+u)*tanhu*sechu/4 From 613fd93f83fcb2c3873e7f291a76a98460e3c9ad Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Tue, 6 Mar 2018 12:06:03 +0000 Subject: [PATCH 17/18] Rename (i)K -> ellip(i)K --- docs/src/index.md | 2 +- src/SpecialFunctions.jl | 2 +- src/elliptic.jl | 24 ++++++++++++------------ test/elliptic.jl | 18 +++++++++--------- test/elliptic/{K.bin => ellipK.bin} | Bin test/elliptic/elliptic.py | 6 +++--- 6 files changed, 26 insertions(+), 26 deletions(-) rename test/elliptic/{K.bin => ellipK.bin} (100%) diff --git a/docs/src/index.md b/docs/src/index.md index d1f24de6..edd7f75b 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -42,7 +42,7 @@ libraries. | [`besselkx(nu,z)`](@ref SpecialFunctions.besselkx) | scaled modified Bessel function of the second kind of order `nu` at `z` | | [`ellipj(u,m)`](@ref SpecialFunctions.ellipj) | Jacobi elliptic functions `sn,cn,dn` | | `jpq(u,m)` | Jacobi elliptic function `pq` | -| [`K(m)`](@ref SpecialFunctions.ellipj) | Complete elliptic integral of the first kind | +| [`ellipK(m)`](@ref SpecialFunctions.ellipj) | Complete elliptic integral of the first kind | ## Installation diff --git a/src/SpecialFunctions.jl b/src/SpecialFunctions.jl index 77585fe5..f438f4ec 100644 --- a/src/SpecialFunctions.jl +++ b/src/SpecialFunctions.jl @@ -73,7 +73,7 @@ export sinint, export ellipj, jss,jsc,jsd,jsn,jcs,jcc,jcd,jcn,jds,jdc,jdd,jdn,jns,jnc,jnd,jnn, - K, iK + ellipK, ellipiK include("bessel.jl") include("elliptic.jl") diff --git a/src/elliptic.jl b/src/elliptic.jl index e6621d27..4d94725c 100644 --- a/src/elliptic.jl +++ b/src/elliptic.jl @@ -199,7 +199,7 @@ end #---------------------------------------------- # Complete elliptic integral of the first kind -function iK_agm(m) +function ellipiK_agm(m) # [1, Sec 17.6] T = typeof(m) m == 0 && return T(Inf) @@ -210,29 +210,29 @@ function iK_agm(m) end return T(π)/(2*a) # https://github.com/JuliaLang/julia/issues/26324 end -iK(m) = iK_agm(float(m)) -K(m::Real) = iK(1-m) -function K(m::Complex) +ellipiK(m) = ellipiK_agm(float(m)) +ellipK(m::Real) = ellipiK(1-m) +function ellipK(m::Complex) # Make sure we hit the "right" branch of sqrt if imag(m) == 0. # Here, "right" is defined as being consistent with mpmath. if imag(m) == 0 - return iK(complex(1-real(m),imag(m))) + return ellipiK(complex(1-real(m),imag(m))) else - return iK(1-m) + return ellipiK(1-m) end end """ - iK(m1) + ellipiK(m1) -Evaluate `K(1-m1)` with better precision for small values of `m1`. +Evaluate `ellipK(1-m1)` with better precision for small values of `m1`. """ -function iK end +function ellipiK end doc""" - K(m) + ellipK(m) -Complete elliptic integral of the first kind. +Complete elliptic integral of the first kind ``K``. ```math \begin{aligned} @@ -242,4 +242,4 @@ Complete elliptic integral of the first kind. \end{aligned} ``` """ -function K end +function ellipK end diff --git a/test/elliptic.jl b/test/elliptic.jl index 4487574e..dcd6e2b5 100644 --- a/test/elliptic.jl +++ b/test/elliptic.jl @@ -104,28 +104,28 @@ end end -@testset "K" begin +@testset "ellipK" begin @testset "type stability" begin realtypes = (Int,Float32,Float64,BigFloat) types = (realtypes..., complex.(realtypes)...) @testset "typeof(m) = $M" for M in types - @inferred K(zero(M)) + @inferred ellipK(zero(M)) end end @testset "special values" begin - @test isnan(K(NaN)) - @test K(Inf+0im) == 0.0 - @test K(-Inf) == 0.0 + @test isnan(ellipK(NaN)) + @test ellipK(Inf+0im) == 0.0 + @test ellipK(-Inf) == 0.0 end @testset "mpmath" begin - open("elliptic/K.bin","r") do f + open("elliptic/ellipK.bin","r") do f npts = ntoh(read(f,Int64)) for i = 1:npts m = read(f, Complex{BigFloat}) - Kref = read(f, Complex{BigFloat}) - @test K(m) ≈ Kref + ellipKref = read(f, Complex{BigFloat}) + @test ellipK(m) ≈ ellipKref end end end @@ -137,7 +137,7 @@ end x = Complex{T}[0; vec(s.*r')] for m0 = (0,1) for m = m0 .+ x - @test K(m) ≈ K(big(m)) rtol=3*eps(T) + @test ellipK(m) ≈ ellipK(big(m)) rtol=3*eps(T) end end end diff --git a/test/elliptic/K.bin b/test/elliptic/ellipK.bin similarity index 100% rename from test/elliptic/K.bin rename to test/elliptic/ellipK.bin diff --git a/test/elliptic/elliptic.py b/test/elliptic/elliptic.py index 7d8f2e1c..22903430 100644 --- a/test/elliptic/elliptic.py +++ b/test/elliptic/elliptic.py @@ -118,7 +118,7 @@ def dump_testset(name,m): f.write(parse_mpc(mi)) f.write(parse_mpc(K)) -def test_K(): +def test_ellipK(): ns = 16 s = [ mpmath.exp(2*mpmath.mp.pi*1j*i/ns) @@ -136,5 +136,5 @@ def test_K(): mpmath.mp.pi, ] m = [mpmath.mpc(0)] + [si*ri for si in s for ri in r] - dump_testset("K.bin",m + [1+mi for mi in m]) -test_K() + dump_testset("ellipK.bin",m + [1+mi for mi in m]) +test_ellipK() From 37e5999c5fe0f778f2de2e41b6beeff339a21cb8 Mon Sep 17 00:00:00 2001 From: Simon Etter Date: Tue, 20 Mar 2018 20:18:14 +0000 Subject: [PATCH 18/18] Fix `puresqrt()` --- src/elliptic.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/elliptic.jl b/src/elliptic.jl index 4d94725c..fcaeffe4 100644 --- a/src/elliptic.jl +++ b/src/elliptic.jl @@ -97,7 +97,7 @@ function ellipj_dispatch(u,m, ::Val{N}) where {N} end end -Base.@pure puresqrt(x) = Float64(sqrt(x)) +Base.@pure puresqrt(x::Float64) = sqrt(x) Base.@pure function nsteps(m,ε) i = 0 while abs(m) > ε @@ -113,7 +113,7 @@ function ellipj_nsteps(u,m) # For all FloatXX types, this can be done at compile time, while for # BigFloat this has to be done at runtime. T = promote_type(typeof(u),typeof(m)) - ε = puresqrt(eps(real(typeof(m)))) + ε = puresqrt(Float64(eps(real(typeof(m))))) N = nsteps(ε,typeof(m)) return ellipj_dispatch(u,m,Val{N}())::NTuple{3,T} end