Compute Wigner 3j and ClebschGordan coefficients
Project description
Calculation of Wigner symbols and related constants
This package computes Wigner 3j coefficients and ClebschGordan coefficients in
pure Rust. The calculation is based on the prime factorization of the different
factorials involved in the coefficients, keeping the values in a rational root
form (sign * \sqrt{s / n}
) for as long as possible. This idea for the
algorithm is described in:
H. T. Johansson and C. Forssén, SIAM Journal on Scientific Compututing 38 (2016) 376384
This implementation takes a lot of inspiration from the WignerSymbols Julia implementation (and even started as a direct translation of it), many thanks to them! This package is available under the same license as the Julia package.
Usage
From python
pip install wigners
And then call one of the exported function:
import wigners
w3j = wigners.wigner_3j(j1, j2, j3, m1, m2, m3)
cg = wigners.clebsch_gordan(j1, m1, j2, m1, j3, m3)
# full array of ClebschGordan coefficients, computed in parallel
cg_array = wigners.clebsch_gordan_array(ji, j2, j3)
# we have an internal cache for recently computed CG coefficients, if you
# need to clean it up you can use this function
wigners.clear_wigner_3j_cache()
From rust
Add this crate to your Cargo.toml
dependencies section:
wigners = "0.3"
And then call one of the exported function:
let w3j = wigners::wigner_3j(j1, j2, j3, m1, m2, m3);
let cg = wigners::clebsch_gordan(j1, m1, j2, m1, j3, m3);
wigners::clear_wigner_3j_cache();
Limitations
Only Wigner 3j symbols for full integers (no halfintegers) are implemented, since that's the only part I need for my own work.
6j and 9j symbols can also be computed with this approach; and support for halfintegers should be feasible as well. I'm open to pullrequest implementing these!
Benchmarks
This benchmark measure the time to compute all possible Wigner 3j symbols up to a fixed maximal angular momentum; clearing up any cached values from previous angular momentum before starting the loop. In pseudo code, the benchmark looks like this:
if cached_wigner_3j:
clear_wigner_3j_cache()
# only measure the time taken by the loop
start = time.now()
for j1 in range(max_angular):
for j2 in range(max_angular):
for j3 in range(max_angular):
for m1 in range(j1, j1 + 1):
for m2 in range(j2, j2 + 1):
for m3 in range(j3, j3 + 1):
w3j = wigner_3j(j1, j2, j3, m1, m2, m3)
elapsed = start  time.now()
Here are the results on an Apple M1 Max (10 cores) CPU:
angular momentum  wigners (this)  wignersymbols v0.5  WignerSymbols.jl v2.0  wigxjpf v1.11  sympy v1.11 

4  0.190 ms  7.50 ms  2.58 ms  0.228 ms  28.7 ms 
8  4.46 ms  227 ms  47.0 ms  7.36 ms  1.36 s 
12  34.0 ms  1.94 s  434 ms  66.2 ms  23.1 s 
16  156 ms  9.34 s  1.98 s  333 ms  / 
20  531 ms  /  6.35 s  1.21 s  / 
A second set of benchmarks checks computing Wigner symbols for large j
, with the
corresponding m
varying from 10 to 10, i.e. in pseudo code:
if cached_wigner_3j:
clear_wigner_3j_cache()
# only measure the time taken by the loop
start = time.now()
for m1 in range(10, 10 + 1):
for m2 in range(10, 10 + 1):
for m3 in range(10, 10 + 1):
w3j = wigner_3j(j1, j2, j3, m1, m2, m3)
elapsed = start  time.now()
(j1, j2, j3)  wigners (this)  wignersymbols v0.5  WignerSymbols.jl v2.0  wigxjpf v1.11  sympy v1.11 

(300, 100, 250)  38.7 ms  16.5 ms  32.9 ms  7.60 ms  2.31 s 
To run the benchmarks yourself on your own machine, execute the following commands:
cd benchmarks
cargo bench # this gives the results for wigners, wignersymbols and wigxjpf
python sympybench.py # this gives the results for sympy
julia wignersymbol.jl # this gives the results for WignerSymbols.jl
Comparison to wignersymbols
There is another Rust implementation of wigner symbols: the
wignersymbols crate.
wignersymbols
also implements 6j and 9j symbols, but it was not usable for my
case since it relies on rug for arbitrary
precision integers and through it on the GMP library. The
GMP library might be problematic for you for one of these reason:
 it is relatively slow (see the benchmarks above)
 it is distributed under LGPL (this crate is distributed under Apache/MIT);
 it is written in C and C++; and as such is hard to crosscompile or compile to WASM;
 it does not support the MSVC compiler on windows, only the GNU compilers
As you can see in the benchmarks above, this usage of GMP becomes an advantage for large j, where the algorithm used in this crate does not scale as well.
License
This crate is distributed under both the MIT license and the Apache 2.0 license.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Hashes for wigners0.3.0py3nonewin_amd64.whl
Algorithm  Hash digest  

SHA256  cdfa809d4e7689f9660868bd42721c9f3d5de0a95c447f145c30d196a6a14020 

MD5  967ad7a301e5bb9479aac9a3c3873dd0 

BLAKE2b256  9b337b9546266a37c64c4638d49c0d77642b3e48639fb52c27463101b096b263 
Hashes for wigners0.3.0py3nonemanylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl
Algorithm  Hash digest  

SHA256  f37d9b2a9486f6ccf79c47c86c643273f21ab32124832b62dc3852d513fca202 

MD5  1551d1dfdc9154cda9fc57c85a1371a2 

BLAKE2b256  0dab4b44de1b6f5e6a9a6173df9175c062ce193c41c2ab816c225ed6ca0ef76a 
Hashes for wigners0.3.0py3nonemacosx_11_0_arm64.whl
Algorithm  Hash digest  

SHA256  54277157d1048dcef4417302217cfc2234ad77eee288fc1324cbf52dc4190672 

MD5  6d63498a1f0e9e45443a9da4f6cf165b 

BLAKE2b256  bcf80bd16263fe8453bc925e02587f62f0f2c52dc36a1452675c2387f367a7fd 
Hashes for wigners0.3.0py3nonemacosx_10_9_x86_64.whl
Algorithm  Hash digest  

SHA256  ada27eb7a79ab0c96e73f37656eadfbb20206452cd6007f545383ecb778bc9f0 

MD5  cb628f232ba93467cd27e90ee482a130 

BLAKE2b256  c0a52f1246c48e24bb665c10d4a8c6f725ae918ee1efad093c32983ec86aa635 