use RationalRoots representation

This commit is contained in:
Jutho Haegeman 2019-08-07 10:09:22 +02:00
parent 80cc592dd3
commit 8ee830e173
6 changed files with 52 additions and 45 deletions

View file

@ -4,14 +4,15 @@ export δ, Δ, clebschgordan, wigner3j, wigner6j, racahV, racahW, HalfInteger
using Base.GMP.MPZ
using HalfIntegers
using RationalRoots
const RRBig = RationalRoot{BigInt}
import RationalRoots: _convert
include("primefactorization.jl")
const Wigner3j = Dict{Tuple{UInt,UInt,UInt,Int,Int},Tuple{Rational{BigInt},Rational{BigInt}}}()
const Wigner6j = Dict{NTuple{6,UInt},Tuple{Rational{BigInt},Rational{BigInt}}}()
const FASTCUTOFF = convert(BigInt, typemax(Int))
function __init__()
global bigone, bigprimetable, Wigner3j, Wigner6j
bigone[] = big(1)
@ -44,8 +45,8 @@ as a type `T` floating point number.
Returns `zero(T)` if the triangle condition `δ(j₁, j₂, j₃)` is not satisfied, but
throws a `DomainError` if the `jᵢ`s are not (half)integer
"""
Δ(j₁, j₂, j₃) = Δ(Float64, j₁, j₂, j₃)
function Δ(T::Type{<:AbstractFloat}, j₁, j₂, j₃)
Δ(j₁, j₂, j₃) = Δ(RRBig, j₁, j₂, j₃)
function Δ(T::Type{<:Real}, j₁, j₂, j₃)
for jᵢ in (j₁, j₂, j₃)
(ishalfinteger(jᵢ) && jᵢ >= zero(jᵢ)) || throw(DomainError("invalid jᵢ", jᵢ))
end
@ -53,7 +54,7 @@ function Δ(T::Type{<:AbstractFloat}, j₁, j₂, j₃)
return zero(T)
end
n, d = Δ²(j₁, j₂, j₃)
return sqrt(convert(T, convert(BigInt, n) // convert(BigInt, d)))
return convert(T, signedroot(RationalRoot{BigInt}, n//d))
end
"""
@ -67,8 +68,8 @@ as a type `T` floating point number. By default, `T = Float64` and `m₃ = -m₁
Returns `zero(T)` if the triangle condition `δ(j₁, j₂, j₃)` is not satisfied, but
throws a `DomainError` if the `jᵢ`s and `mᵢ`s are not (half)integer or `abs(mᵢ) > jᵢ`.
"""
wigner3j(j₁, j₂, j₃, m₁, m₂, m₃ = -m₁-m₂) = wigner3j(Float64, j₁, j₂, j₃, m₁, m₂, m₃)
function wigner3j(T::Type{<:AbstractFloat}, j₁, j₂, j₃, m₁, m₂, m₃ = -m₁-m₂)
wigner3j(j₁, j₂, j₃, m₁, m₂, m₃ = -m₁-m₂) = wigner3j(RRBig, j₁, j₂, j₃, m₁, m₂, m₃)
function wigner3j(T::Type{<:Real}, j₁, j₂, j₃, m₁, m₂, m₃ = -m₁-m₂)
# check angular momenta
for (jᵢ,mᵢ) in ((j₁, m₁), (j₂, m₂), (j₃, m₃))
ϵ(jᵢ, mᵢ) || throw(DomainError((jᵢ, mᵢ), "invalid combination (jᵢ, mᵢ)"))
@ -104,13 +105,7 @@ function wigner3j(T::Type{<:AbstractFloat}, j₁, j₂, j₃, m₁, m₂, m₃ =
s *= compute3jseries(β₁, β₂, β₃, α₁, α₂)
Wigner3j[(β₁, β₂, β₃, α₁, α₂)] = (r,s)
end
if T != BigFloat && all((s.num, s.den, r.num, r.den) .< FASTCUTOFF)
sn, sd, rn, rd = convert.(T, (s.num, s.den, r.num, r.den))
return sgn*(sn/sd)*sqrt(rn/rd)
else
return convert(T, sgn*s*sqrt(r))
end
return _convert(T, sgn*s)*convert(T, signedroot(r))
end
"""
@ -123,11 +118,11 @@ Returns `zero(T)` if the triangle condition `δ(j₁, j₂, j₃)` is not satisf
throws a `DomainError` if the `jᵢ`s and `mᵢ`s are not (half)integer or `abs(mᵢ) > jᵢ`.
"""
clebschgordan(j₁, m₁, j₂, m₂, j₃, m₃ = m₁+m₂) =
clebschgordan(Float64, j₁, m₁, j₂, m₂, j₃, m₃)
function clebschgordan(T::Type{<:AbstractFloat}, j₁, m₁, j₂, m₂, j₃, m₃ = m₁+m₂)
clebschgordan(RRBig, j₁, m₁, j₂, m₂, j₃, m₃)
function clebschgordan(T::Type{<:Real}, j₁, m₁, j₂, m₂, j₃, m₃ = m₁+m₂)
s = wigner3j(T, j₁, j₂, j₃, m₁, m₂, -m₃)
iszero(s) && return s
s *= sqrt(convert(T, j₃+j₃+one(j₃)))
s *= convert(T, signedroot(RRBig, j₃+j₃+one(j₃)))
return isodd(convert(Int,j₁ - j₂ + m₃)) ? -s : s
end
@ -142,8 +137,8 @@ as a type `T` floating point number. By default, `T = Float64` and `m₃ = -m₁
Returns `zero(T)` if the triangle condition `δ(j₁, j₂, j₃)` is not satisfied, but
throws a `DomainError` if the `jᵢ`s and `mᵢ`s are not (half)integer or `abs(mᵢ) > jᵢ`.
"""
racahV(j₁, j₂, j₃, m₁, m₂, m₃ = -m₁-m₂) = racahV(Float64, j₁, j₂, j₃, m₁, m₂, m₃)
function racahV(T::Type{<:AbstractFloat}, j₁, j₂, j₃, m₁, m₂, m₃ = -m₁-m₂)
racahV(j₁, j₂, j₃, m₁, m₂, m₃ = -m₁-m₂) = racahV(RRBig, j₁, j₂, j₃, m₁, m₂, m₃)
function racahV(T::Type{<:Real}, j₁, j₂, j₃, m₁, m₂, m₃ = -m₁-m₂)
s = wigner3j(T, j₁, j₂, j₃, m₁, m₂, m₃)
return isodd(convert(Int, -j₁ + j₂ + j₃)) ? -s : s
end
@ -160,8 +155,8 @@ Returns `zero(T)` if any of triangle conditions `δ(j₁, j₂, j₃)`, `δ(j₁
`δ(j₂, j₄, j₆)`, `δ(j₃, j₄, j₅)` are not satisfied, but throws a `DomainError` if
the `jᵢ`s are not integer or halfinteger.
"""
wigner6j(j₁, j₂, j₃, j₄, j₅, j₆) = wigner6j(Float64, j₁, j₂, j₃, j₄, j₅, j₆)
function wigner6j(T::Type{<:AbstractFloat}, j₁, j₂, j₃, j₄, j₅, j₆)
wigner6j(j₁, j₂, j₃, j₄, j₅, j₆) = wigner6j(RRBig, j₁, j₂, j₃, j₄, j₅, j₆)
function wigner6j(T::Type{<:Real}, j₁, j₂, j₃, j₄, j₅, j₆)
# check validity of `jᵢ`s
for jᵢ in (j₁, j₂, j₃, j₄, j₅, j₆)
(ishalfinteger(jᵢ) && jᵢ >= zero(jᵢ)) || throw(DomainError("invalid jᵢ", jᵢ))
@ -201,19 +196,15 @@ function wigner6j(T::Type{<:AbstractFloat}, j₁, j₂, j₃, j₄, j₅, j₆)
snum, rnum = splitsquare(n₁ * n₂ * n₃ * n₄)
sden, rden = splitsquare(d₁ * d₂ * d₃ * d₄)
snu, sden = divgcd!(snum, sden)
rnu, rden = divgcd!(rnum, rden)
s = convert(BigInt, snum) // convert(BigInt, sden)
r = convert(BigInt, rnum) // convert(BigInt, rden)
s *= compute6jseries(β₁, β₂, β₃, α₁, α₂, α₃, α₄)
Wigner6j[(β₁, β₂, β₃, α₁, α₂, α₃)] = (r, s)
end
if T != BigFloat && all((s.num, s.den, r.num, r.den) .< FASTCUTOFF)
sn, sd, rn, rd = convert.(T, (s.num, s.den, r.num, r.den))
return (sn/sd)*sqrt(rn/rd)
else
return convert(T, s*sqrt(r))
end
return _convert(T, s) * convert(T, signedroot(r))
end
"""
@ -227,8 +218,8 @@ Returns `zero(T)` if any of triangle conditions `δ(j₁, j₂, J₁₂)`, `δ(j
`δ(j₁, J₂₃, J)`, `δ(J₁₂, j₃, J)` are not satisfied, but throws a `DomainError` if
the `jᵢ`s and `J`s are not integer or halfinteger.
"""
racahW(j₁, j₂, J, j₃, J₁₂, J₂₃) = racahW(Float64, j₁, j₂, J, j₃, J₁₂, J₂₃)
function racahW(T::Type{<:AbstractFloat}, j₁, j₂, J, j₃, J₁₂, J₂₃)
racahW(j₁, j₂, J, j₃, J₁₂, J₂₃) = racahW(RRBig, j₁, j₂, J, j₃, J₁₂, J₂₃)
function racahW(T::Type{<:Real}, j₁, j₂, J, j₃, J₁₂, J₂₃)
s = wigner6j(T, j₁, j₂, J₁₂, j₃, J, J₂₃)
if !iszero(s) && isodd(convert(Int, j₁ + j₂ + j₃ + J))
return -s

View file

@ -116,13 +116,13 @@ Base.promote_rule(P::Type{<:PrimeFactorization},::Type{BigInt}) = BigInt
Base.convert(P::Type{<:PrimeFactorization}, n::Integer) = convert(P, primefactor(n))
function Base.convert(::Type{BigInt}, a::PrimeFactorization)
A = big(a.sign)
A = one(BigInt)
for (n, e) in enumerate(a.powers)
if !iszero(e)
MPZ.mul!(A, bigprime(n, e))
end
end
return A
return a.sign < 0 ? MPZ.neg!(A) : A
end
Base.convert(::Type{PrimeFactorization{U}}, a::PrimeFactorization{U}) where {U<:Unsigned} =
a