WarpPI/core/src/main/java/org/nevec/rjm/Wigner3j.java

629 lines
19 KiB
Java

package org.nevec.rjm;
import java.math.BigInteger;
import java.util.Scanner;
import it.cavallium.warppi.util.Error;
/**
* Exact representations of Wigner 3jm and 3nj values of half-integer arguments.
*
* @see R. J. Mathar, <a href="http://arxiv.org/abs/1102.5125">Corrigendum to
* "Universal factorzation fo 3n-j (j>2) symbols ..[J. Phys. A: Math. Gen.37 (2004) 3259]"
* </a>
* @see R. J. Mathar, <a href="http://vixra.org/abs/1202.0093">Symmetries in
* Wigner 18-j and 21-j Symbols</a>
* @since 2011-02-15
* @author Richard J. Mathar
*/
public class Wigner3j {
/**
* Test programs. This supports three types of direct evaluations:<br>
* java -cp . org.nevec.rjm.Wigner3j 3jm 2j1+1 2j2+1 2j3+1 2m1+1 2m2+1 2m3+1
* <br>
* java -cp . org.nevec.rjm.Wigner3j 6j 2j1+1 2j2+2 .. 2j6+1<br>
* java -cp . org.nevec.rjm.Wigner3j 9j 2j1+1 2j2+2 .. 2j9+1<br>
* The first command line argument is one of the three tags which determine
* whether a 3jm, a 6j or a 9j symbol will be computed. The other arguments
* are 6 or 9 integer values, which are the physical (half-integer) values
* multplied by 2 and augmented by 1. The order of the 6 or 9 values is as
* reading the corresponding standard symbol as first row, then second row
* (and for the 9j symbol) third row.
*
* @since 2011-02-15
* @author Richard J. Mathar
* @throws Error
*/
static public void main(final String args[]) throws Error {
if (args[0].compareTo("6j") == 0) {
try {
final String m1 = "6";
final String t1 = "1 2 -3 -1 5 6";
final String t2 = "4 -5 3 -4 -2 -6";
String j = "";
for (int i = 1; i <= 6; i++) {
j += args[i] + " ";
}
final BigSurdVec w = Wigner3j.wigner3j(m1, t1, t2, j);
System.out.println(w.toString());
} catch (final Exception e) {
System.out.println(e.getMessage());
}
} else if (args[0].compareTo("9j") == 0) {
try {
final String m1 = "9";
final String t1 = "1 3 2 4 6 5 7 9 8";
final String t2 = "2 8 5 6 3 9 7 4 1";
String j = "";
for (int i = 1; i <= 9; i++) {
j += args[i] + " ";
}
final BigSurdVec w = Wigner3j.wigner3j(m1, t1, t2, j);
System.out.println(w.toString());
} catch (final Exception e) {
System.out.println(e.getMessage());
}
} else if (args[0].compareTo("3jm") == 0) {
final int j1 = new Integer(args[1]).intValue();
final int j2 = new Integer(args[2]).intValue();
final int j3 = new Integer(args[3]).intValue();
final int m1 = new Integer(args[4]).intValue();
final int m2 = new Integer(args[5]).intValue();
final int m3 = new Integer(args[6]).intValue();
try {
BigSurd w = Wigner3j.wigner3jm(j1, j2, j3, m1, m2, m3);
System.out.println(w.toString());
w = w.multiply(new BigSurd(j3 + 1, 1));
System.out.println("CG factor sqrt" + (j3 + 1) + "sign " + (j2 - j2 - m3) / 2 + " " + w.toString());
} catch (final Exception e) {
System.out.println(e.getMessage());
}
} else {
System.out.println("usage:");
System.out.println(args[0] + " 6j 2j1+1 2j2+1 2j3+1 2j4+1 2j5+1 2j6+1");
System.out.println(args[0] + " 9j 2j1+1 2j2+1 2j3+1 2j4+1 2j5+1 2j6+1.. 2j9+1 ");
System.out.println(args[0] + " 3jm 2j1+1 2j2+1 2j3+1 2m1+1 2m2+1 2m3+1 ");
}
} /* Wigner3j.main */
/**
* The Wigner 3jm symbol (j1,j2,j3,m1,m2,m3). All arguments of the function
* are the actual parameters multiplied by 2, so they all allow an integer
* representation.
*
* @param j1
* integer representing 2*j1
* @param j2
* integer representing 2*j2
* @param j3
* integer representing 2*j3
* @param m1
* integer representing 2*m1
* @param m2
* integer representing 2*m2
* @param m3
* integer representing 2*m3
* @return The value of the symbol. Zero if any of the triangular
* inequalities is violated or some parameters are out of range.
* @since 2011-02-13
* @author Richard J. Mathar
* @throws Error
*/
static public BigSurd wigner3jm(final int j1, final int j2, final int j3, final int m1, final int m2, final int m3)
throws Error {
final Rational J1 = new Rational(j1, 2);
final Rational J2 = new Rational(j2, 2);
final Rational J3 = new Rational(j3, 2);
final Rational M1 = new Rational(m1, 2);
final Rational M2 = new Rational(m2, 2);
final Rational M3 = new Rational(m3, 2);
return Wigner3j.wigner3jm(J1, J2, J3, M1, M2, M3);
} /* wigner3jm */
/**
* Wigner 3jn symbol. For the 6j symbol, the input of the 3 lines is
* "1 2 3 1 5 6", "4 5 3 4 2 6" "2j1+1 2j2+1 2j3+1 2l1+1 2l2+1 2l3+1"
*
* @param m1
* The information on the number of angular momenta.
* @param t1
* The list of one half of the triads, indexing j, whitespace
* separated
* @param t2
* The list of the second half of the triads, indexing j,
* whitespace separated
* @param j
* The list of the integer values of the angular momenta. They
* are actually the doubled j-values plus 1, whitespace
* separated. Only as many as announced by the m1 parameter are
* used; trailing numbers are ignored.
* @see A. Bar-Shalom and M. Klapisch,
* <a href="https://doi.org/10.1016/0010-4655(88)90192-0">NJGRAF...
* </a>, Comp. Phys Comm. 50 (3) (1988) 375
* @since 2011-02-13
* @since 2012-02-15 Upgraded return value to BigSurdVec
* @author Richard J. Mathar
* @throws Error
*/
static public BigSurdVec wigner3j(final String m1, final String t1, final String t2, final String j) throws Error {
/*
* The first number in the line "m" is the number of angular momenta.
* The rest of the line is ignored.
*/
Scanner s = new Scanner(m1);
final int m = s.nextInt();
if (m % 3 != 0) {
s.close();
throw new IllegalArgumentException("Angular momenta " + m + " not a multiple of three.");
}
/*
* Scan the numbers in the line "j". Excess numbers beyond what has been
* announced in the "m" line are ignored.
*/
final int[] jvec = new int[m];
final int[] tvec = new int[2 * m];
s.close();
/*
* the third row contains positive 2j+1.
*/
s = new Scanner(j);
int ji = 0;
while (s.hasNextInt() && ji < m) {
jvec[ji++] = s.nextInt();
if (jvec[ji - 1] < 1) {
s.close();
throw new IllegalArgumentException("Illegal value " + jvec[ji - 1] + " for 2j+1.");
}
}
s.close();
/*
* the first two rows contain signed values of indices into the j list
*/
s = new Scanner(t1);
int ti = 0;
while (s.hasNextInt()) {
tvec[ti++] = s.nextInt();
}
s.close();
s = new Scanner(t2);
while (s.hasNextInt()) {
tvec[ti++] = s.nextInt();
}
/*
* Basic sanity checks. All indices in the first two lines address a
* number in the third line, and each index occurs exactly twice.
*/
if (ji % 3 != 0) {
s.close();
throw new IllegalArgumentException("j-count " + ji + " not a multiple of three.");
}
if (ti != 2 * ji) {
s.close();
throw new IllegalArgumentException("triad-count " + ti + " not twice j-count " + ji);
}
final int[] jfreq = new int[m];
for (ji = 0; ji < jfreq.length; ji++) {
jfreq[ji] = 0;
}
/*
* maintain a 0-based index which shows where the j-value has its first
* and second occurrence in the flattened list of triads.
*/
final int[][] jhash = new int[m][2];
for (ti = 0; ti < 2 * m; ti++) {
final int t = tvec[ti];
if (t == 0 || Math.abs(t) > jvec.length) {
s.close();
throw new IllegalArgumentException("Triad index " + t + " out of bounds");
}
if (jfreq[Math.abs(t) - 1] >= 2) {
s.close();
throw new IllegalArgumentException("Node " + t + " referenced more than twice");
}
jhash[Math.abs(t) - 1][jfreq[Math.abs(t) - 1]] = ti;
jfreq[Math.abs(t) - 1]++;
}
/*
* Move on from the 2j+1 values of the input to the j-values. Subtract
* one and divide through 2.
*/
final Rational[] J = new Rational[jvec.length];
for (ji = 0; ji < jvec.length; ji++) {
J[ji] = new Rational(jvec[ji] - 1, 2);
}
/*
* Convert the 1-based indices to 0-based indices, loosing the sign
* information.
*/
final int[] triadidx = new int[tvec.length];
for (ti = 0; ti < tvec.length; ti++) {
triadidx[ti] = Math.abs(tvec[ti]) - 1;
}
/*
* The M-values are all null (undetermined) at the start.
*/
final Rational[] M = new Rational[J.length];
s.close();
return Wigner3j.wigner3j(tvec, J, M, triadidx);
} /* wigner3j */
/**
* Wigner 3jn symbol. Computes sum_{mi} (-1)^(j1-m1+j2-m2+...)
* triad(triadidx[0..2])*triad(triadidx[3..5])*... where each factor is a
* Wigner-3jm symbol with each sign of m_i occurring once at the
* corresponding l-value.
*
* @param triadidx
* 0-based indices into the list of J
* @param J
* The list of J-values
* @param M
* The list of M-values associated with the J. This contains null
* where the parameter has not yet been set by an outer loop.
* @since 2011-02-13
* @since 2012-02-15 Upgraded to return BigSurdVec
* @author Richard J. Mathar
* @throws Error
*/
static private BigSurdVec wigner3j(final int[] tvec, final Rational[] J, final Rational[] M, final int[] triadidx)
throws Error {
/*
* The result of the computation. The sum over all m-combinations of the
* triads.
*/
BigSurdVec res = new BigSurdVec();
/*
* First step is to monitor the triangular conditions on the J. If at
* least one is violated, the result is zero. Loop over the triads.
*/
for (int t = 0; t < triadidx.length; t += 3) {
/* Ensure |J[t]-J[t+1]| <= J[t+2] <= J[t]+J[t+1] */
if (J[triadidx[t]].subtract(J[triadidx[t + 1]]).abs().compareTo(J[triadidx[t + 2]]) > 0) {
return res;
}
if (J[triadidx[t]].add(J[triadidx[t + 1]]).compareTo(J[triadidx[t + 2]]) < 0) {
return res;
}
}
/*
* the index of the preferred member of the triad list. Preference given
* to those dangling in triads where alreaday two others are fixed, then
* to members where at least one is fixed, then to smallest associated
* J-values.
*/
int freeM = -1;
int freeMrank = -1;
for (int i = 0; i < triadidx.length; i++) {
/*
* found an m-value which has not yet been summed over.
*/
if (M[triadidx[i]] == null) {
/*
* two cases: value is fixed implicitly because already two
* others values are set in the triad. or it is still to
* maintain its own explicit loop.
*/
final int triadn = i / 3;
final int triadr = i % 3;
/*
* the neighbors in the triad have indices triadn*3+ (tiradr+1)
* mod 3 and triadn*3+(triadr+2) mod3
*/
final int nei1 = 3 * triadn + (triadr + 1) % 3;
final int nei2 = 3 * triadn + (triadr + 2) % 3;
/*
* found a candidate for which the two other values are already
* set.
*/
if (M[triadidx[nei1]] != null && M[triadidx[nei2]] != null) {
freeM = i;
break;
} else {
/*
* rough work load estimator: basically (2J1+1)*(2J2+1)
*/
Rational wt = J[triadidx[i]].multiply(2).add(1);
if (M[triadidx[nei1]] == null) {
wt = wt.multiply(J[triadidx[nei1]].multiply(2).add(1));
}
if (M[triadidx[nei2]] == null) {
wt = wt.multiply(J[triadidx[nei2]].multiply(2).add(1));
}
final int thiswt = wt.intValue();
if (freeM < 0 || thiswt < freeMrank) {
freeM = i;
freeMrank = thiswt;
}
}
}
}
if (freeM >= 0) {
/*
* found an m-value which has not yet been summed over.
*/
if (M[triadidx[freeM]] == null) {
final Rational[] childM = new Rational[M.length];
for (int ji = 0; ji < M.length; ji++) {
if (M[ji] != null) {
childM[ji] = M[ji];
}
}
/*
* two cases: value is fixed implicitly because already two
* others values are set in the triad. or it is still to
* maintain its own explicit loop.
*/
final int triadn = freeM / 3;
final int triadr = freeM % 3;
/*
* the neighbors in the triad have indices triadn*3+ (triadr+1)
* mod 3 and triadn*3+(triadr+2) mod3
*/
final int nei1 = 3 * triadn + (triadr + 1) % 3;
final int nei2 = 3 * triadn + (triadr + 2) % 3;
if (M[triadidx[nei1]] == null || M[triadidx[nei2]] == null) {
/*
* The J-value is J[triadidx[freeM]]. Loop from -J to +J,
* the allowed range.
*/
Rational newm = J[triadidx[freeM]].negate();
while (newm.compareTo(J[triadidx[freeM]]) <= 0) {
childM[triadidx[freeM]] = tvec[freeM] > 0 ? newm : newm.negate();
res = res.add(Wigner3j.wigner3j(tvec, J, childM, triadidx));
newm = newm.add(Rational.ONE);
}
} else {
/*
* Set its value and the value at its companion j-value. Sum
* of the three m-values in the triad is to be zero for a
* non-zero contribution.
*/
Rational m1 = M[triadidx[nei1]];
Rational m2 = M[triadidx[nei2]];
/*
* negate if these are the second occurrences of the J in
* the triads
*/
if (tvec[nei1] < 0) {
m1 = m1.negate();
}
if (tvec[nei2] < 0) {
m2 = m2.negate();
}
/* m3 = -(m1+m2) */
final Rational newm = tvec[freeM] > 0 ? m1.add(m2).negate() : m1.add(m2);
/*
* No contribution if the m-value enforced by the other two
* entries is outside the range -|J|..|J| enforced by its
* associated J-value. One could essentially remove this
* branching and let wigner3j() decide on this, but this is
* inefficient.
*/
if (newm.abs().compareTo(J[triadidx[freeM]]) <= 0) {
childM[triadidx[freeM]] = newm;
res = res.add(Wigner3j.wigner3j(tvec, J, childM, triadidx));
}
/*
* zero contribution if this m-value cannot be set to any
* value compatible with the triangular conditions.
*/
}
return res;
}
}
/*
* reached the bottom of the loop where all M-values are assigned. Build
* the product over all Wigner-3jm values and the associated sign.
*/
res = BigSurdVec.ONE;
for (int ji = 0; ji < triadidx.length; ji += 3) {
Rational m1 = M[triadidx[ji]];
Rational m2 = M[triadidx[ji + 1]];
Rational m3 = M[triadidx[ji + 2]];
/*
* negate if these are associated with in-flowing vectors in the
* triads
*/
if (tvec[ji] < 0) {
m1 = m1.negate();
}
if (tvec[ji + 1] < 0) {
m2 = m2.negate();
}
if (tvec[ji + 2] < 0) {
m3 = m3.negate();
}
res = res.multiply(Wigner3j.wigner3jm(J[triadidx[ji]], J[triadidx[ji + 1]], J[triadidx[ji + 2]], m1, m2, m3));
/*
* if a partial product yields zero, the total product is zero, too,
* and offers an early exit.
*/
if (res.signum() == 0) {
return BigSurdVec.ZERO;
}
}
/*
* The overal sign is product_{J-Mpairs} (-1)^(J-M). This is an integer
* because all the J-M are integer.
*/
Rational sig = new Rational();
for (int ji = 0; ji < J.length; ji++) {
sig = sig.add(J[ji]).subtract(M[ji]);
}
/*
* sign depends on the sum being even or odd. We assume that "sig" is
* integer and look only at the numerator
*/
if (sig.a.abs().testBit(0)) {
res = res.negate();
}
return res;
} /* wigner3j */
/**
* The Wigner 3jm symbol (j1,j2,j3,m1,m2,m3). Warning: there is no check
* that each argument is indeed half-integer.
*
* @param j1
* integer or half-integer j1
* @param j2
* integer or half-integer j2
* @param j3
* integer or half-integer j3
* @param m1
* integer or half-integer m1
* @param m2
* integer or half-integer m2
* @param m3
* integer or half-integer m3
* @return The value of the symbol. Zero if any of the triangular
* inequalities is violated or some parameters are out of range.
* @since 2011-02-13
* @author Richard J. Mathar
* @throws Error
*/
static protected BigSurd wigner3jm(final Rational j1, final Rational j2, final Rational j3, final Rational m1,
final Rational m2, final Rational m3) throws Error {
/*
* Check that m1+m2+m3 = 0
*/
if (m1.add(m2).add(m3).signum() != 0) {
return BigSurd.ZERO;
}
/*
* Check that j1+j2+j3 is integer
*/
if (j1.add(j2).add(j3).isBigInteger() == false) {
return BigSurd.ZERO;
}
/*
* Check that |j1-j2|<=j3 <= |j1+j2|
*/
final Rational j1m2 = j1.subtract(j2);
if (j1m2.abs().compareTo(j3) > 0) {
return BigSurd.ZERO;
}
final Rational j1p2 = j1.add(j2);
if (j1p2.abs().compareTo(j3) < 0) {
return BigSurd.ZERO;
}
/*
* Check that |m_i| <= j_i
*/
if (m1.abs().compareTo(j1) > 0 || m2.abs().compareTo(j2) > 0 || m3.abs().compareTo(j3) > 0) {
return BigSurd.ZERO;
}
/*
* Check that m_i-j_i are integer.
*/
if (!m1.subtract(j1).isBigInteger() || !m2.subtract(j2).isBigInteger() || !m3.subtract(j3).isBigInteger()) {
return BigSurd.ZERO;
}
/*
* (-)^(j1-j2-m3)*delta(-m3,m1+m2)*sqrt[ (j3+j1-j2)! (j3-j1+j2)!
* (j1+j2-j3)! /(j1+j2+j3+1)!
* *(j3-m)!*(j3+m)!(j1-m1)!*(j1+m1)!*(j2-m2)!*(j2+m2)!] *sum_k
* (-1)^k/[k!(j1+j2-j3-k)!(j1-m1-k)!(j2+m2-k)!(j3-j2+m1+k)!)*(j3-j1-m2+k
* )!]
*/
/*
* It is tacitly assumed that all the major j_i, m_i values are in the
* integer range. This is implicitly plausible since otherwise the
* execution times of the following loop over the k-values would be
* immense.
*/
int j1j2jk = j1p2.subtract(j3).intValue();
int j1m1k = j1.subtract(m1).intValue();
int j2m2k = j2.add(m2).intValue();
int jj2m1k = j3.subtract(j2).add(m1).intValue();
int jj1m2k = j3.subtract(j1).subtract(m2).intValue();
int k = Math.max(0, -jj2m1k);
k = Math.max(k, -jj1m2k);
if (k > 0) {
j1j2jk -= k;
j1m1k -= k;
j2m2k -= k;
jj2m1k += k;
jj1m2k += k;
}
final Factorial f = new Factorial();
Rational sumk = new Rational();
while (true) {
final BigInteger d = f.at(k).multiply(f.at(j1j2jk)).multiply(f.at(j1m1k)).multiply(f.at(j2m2k)).multiply(f.at(jj2m1k)).multiply(f.at(jj1m2k));
if (k % 2 == 0) {
sumk = sumk.add(new Rational(BigInteger.ONE, d));
} else {
sumk = sumk.subtract(new Rational(BigInteger.ONE, d));
}
j1j2jk--;
j1m1k--;
j2m2k--;
jj2m1k++;
jj1m2k++;
if (j1j2jk < 0 || j1m1k < 0 || j2m2k < 0) {
break;
}
k++;
}
/*
* sign factor (-1)^(j1-j2-m3)
*/
if (j1m2.subtract(m3).intValue() % 2 != 0) {
sumk = sumk.negate();
}
k = j1m2.add(j3).intValue();
BigInteger s = f.at(k);
k = j3.subtract(j1m2).intValue();
s = s.multiply(f.at(k));
k = j1p2.subtract(j3).intValue();
s = s.multiply(f.at(k));
k = j3.add(m3).intValue();
s = s.multiply(f.at(k));
k = j3.subtract(m3).intValue();
s = s.multiply(f.at(k));
k = j1.add(m1).intValue();
s = s.multiply(f.at(k));
k = j1.subtract(m1).intValue();
s = s.multiply(f.at(k));
k = j2.add(m2).intValue();
s = s.multiply(f.at(k));
k = j2.subtract(m2).intValue();
s = s.multiply(f.at(k));
k = j1p2.add(j3).intValue();
k++;
final Rational disc = new Rational(s, f.at(k));
return new BigSurd(sumk, disc);
} /* wigner3jm */
} /* Wigner3j */