From 5e5c37623f51fceb2c4aa16bec88b67d5600fcdc Mon Sep 17 00:00:00 2001
From: Stamatios Sotiropoulos <stam@fmrib.ox.ac.uk>
Date: Mon, 30 Jan 2012 15:07:51 +0000
Subject: [PATCH] Functions for approximating Bingham and Watson distribution
 constants

---
 Bingham_Watson_approx.cc | 346 +++++++++++++++++++++++++++++++++++++++
 Bingham_Watson_approx.h  |  94 +++++++++++
 2 files changed, 440 insertions(+)
 create mode 100755 Bingham_Watson_approx.cc
 create mode 100755 Bingham_Watson_approx.h

diff --git a/Bingham_Watson_approx.cc b/Bingham_Watson_approx.cc
new file mode 100755
index 0000000..9f2c501
--- /dev/null
+++ b/Bingham_Watson_approx.cc
@@ -0,0 +1,346 @@
+/*  Directional Statistics Functions
+
+    Bingham and Watson Distributions and functions to approximate their normalizing constant
+    
+    Stam Sotiropoulos  - FMRIB Image Analysis Group
+ 
+    Copyright (C) 2011 University of Oxford  */
+
+/*  CCOPYRIGHT  */
+
+#include "Bingham_Watson_approx.h"
+
+
+  //Cubic root
+  float croot(const float& x){
+    float res;
+    if (x>=0) 
+      res=pow(x,INV3);
+    else
+      res=-(pow(-x,INV3));
+    return res;
+  }
+
+
+  //Saddlepoint approximation of confluent hypergeometric function of a matrix argument B (Kume & Wood, 2005)
+  //Vector x has the eigenvalues of B.
+  float hyp_Sapprox(ColumnVector &x){
+    float c1;
+
+    //SortDescending(x);         //Not needed??
+    if (x(1)==0 && x(2)==0 && x(3)==0)
+      c1=1;
+    else{
+      float t=find_t(x);
+      float T,R=1, K2=0, K3=0, K4=0;
+      for (int i=1; i<=3; i++){
+        R=R/sqrt(-x(i)-t);
+	K2=K2+1.0/(2*pow(x(i)+t,2.0));
+	K3=K3-1.0/pow(x(i)+t,3.0);
+	K4=K4+3.0/pow(x(i)+t,4.0);
+      }
+      T=K4/(8*K2*K2)-5*K3*K3/(24*K2*K2*K2);  
+      //c1=sqrt(2.0/K2)*pi*R*exp(-t);
+      //float c3=c1*exp(T);
+      //c1=c3/(4*pi);
+      c1=0.25*sqrt(2.0/K2)*R*exp(-t+T);
+    }
+    return c1;
+  }
+
+
+  //Saddlepoint approximation of confluent hypergeometric function of a matrix argument, with its eigenvalues being l1,l1,l2 or l1,l2,l2 with l1!=l2.
+  //Vector x has the three eigenvalues. This function can be also used to approximate a confluent hypergeometric function of a scalar argument k
+  //by providing x=[k 0 0].
+  float hyp_Sapprox_twoequal(ColumnVector& x){
+    float c1, R, t, Bm, Bp, K2, K3, K4,T,q;
+
+    //SortDescending(x);  //Not needed??
+    if (x(1)==x(2)){
+      q=1;
+      x(2)=x(3);
+    }
+    else 
+      q=2;
+
+    R=sqrt(4*(x(1)-x(2))*(x(1)-x(2))+9+4*(2*q-3)*(x(2)-x(1)));
+    t=(-2*(x(1)+x(2))-3-R)/4.0;
+    Bm=1/(R+3-2*(x(2)-x(1)));
+    Bp=1/(R+3+2*(x(2)-x(1)));
+    K2=1.0/(2*pow(x(1)+t,2.0))+1.0/pow(x(2)+t,2.0);
+    K3=-1.0/pow(x(1)+t,3.0)-2.0/pow(x(2)+t,3.0);
+    K4=3.0/pow(x(1)+t,4.0)+6.0/pow(x(2)+t,4.0);
+    T=K4/(8.0*K2*K2)-5.0*K3*K3/(24.0*K2*K2*K2);
+    c1=sqrt(pow(Bm,q-1)*pow(Bp,2-q)/R)*exp(-t+T);
+    return c1;
+  }
+
+
+
+ //Saddlepoint approximation of the ratio of two hypergeometric functions, with matrix arguments L and B (3x3). Vectors xL & xB contain the eigenvalues of L and B. 
+ //Used for the ball & Binghams model, B has two non-zero eigenvalues that represent fanning indices.
+  float hyp_SratioB(ColumnVector& xL,ColumnVector& xB){
+    float c, T1, t1, Norm1, T2,t2,Norm2;
+    float R,K2,K3,K4,tmp,tmp2,tmp3;
+
+    //Approximate Numerator 
+    if (xL(1)==0 && xL(2)==0 && xL(3)==0){
+      Norm1=1; t1=0; T1=0; 
+    }
+    else{
+      //SortDescending(xL);  //Not needed, performed by eigen-decomposition?
+      t1=find_t(xL);
+      R=1; K2=0; K3=0; K4=0; 
+      for (int i=1; i<=3; i++){
+        tmp=xL(i)+t1;
+        tmp2=tmp*tmp; tmp3=tmp2*tmp;
+        R=-R*tmp;
+        K2=K2+0.5/tmp2;
+        K3=K3-1.0/tmp3;
+        K4=K4+3.0/(tmp3*tmp);
+      }
+      T1=K4/(8*K2*K2)-5*K3*K3/(24.0*K2*K2*K2);  
+      Norm1=K2*R;
+    }
+    
+    //Approximate Denominator
+    //SortDescending(xB);  //Not needed, performed by eigen-decomposition?
+    t2=find_t(xB);
+    R=1; K2=0; K3=0; K4=0; 
+    for (int i=1; i<=3; i++){
+      tmp=xB(i)+t2;
+      tmp2=tmp*tmp; tmp3=tmp2*tmp;
+      R=-R*tmp;
+      K2=K2+0.5/tmp2;
+      K3=K3-1.0/tmp3;
+      K4=K4+3.0/(tmp3*tmp);
+    }
+    T2=K4/(8*K2*K2)-5*K3*K3/(24*K2*K2*K2);  
+    Norm2=K2*R;
+
+    //Final Ratio
+    c=sqrt(Norm2/Norm1)*exp(-t1+T1+t2-T2);
+    return c;
+  }
+
+
+  //Saddlepoint aproximation of the ratio ot two hypergeometric functions with matrix arguments L and B in two steps: First denominator, then numerator. 
+  //This allows them to be updated independently, used for the ball & Binghams model to compute the likelihood faster.
+  //This function returns values used in the denominator approximation. xB containes the two non-zero eigenvalues of matrix B.
+  ReturnMatrix approx_denominatorB(ColumnVector& xB){
+    float t2,R,K2,K3,K4,tmp,tmp2,tmp3, T2, Norm2;
+    ColumnVector Res(3);
+
+    SortDescending(xB); //Not needed?
+    t2=find_t(xB);
+    R=1; K2=0; K3=0; K4=0; 
+    for (int i=1; i<=3; i++){
+      tmp=xB(i)+t2;
+      tmp2=tmp*tmp; tmp3=tmp2*tmp;
+      R=-R*tmp;
+      K2=K2+0.5/tmp2;
+      K3=K3-1.0/tmp3;
+      K4=K4+3.0/(tmp3*tmp);
+    }
+    T2=K4/(8.0*K2*K2)-5.0*K3*K3/(24.0*K2*K2*K2);  
+    Norm2=K2*R;
+    Res<< t2 << T2 << Norm2;
+    
+    Res.Release();
+    return Res;
+  }
+
+
+  //Second step for saddlepoint approximation of the ratio of two hypergeometric functions with matrix arguments L and B (xL has the eigenvalues of L). 
+  //Assume that the denominator has already been approximated by the function above and the parameters are stored in denomvals.
+  //Here approximate the numerator and return the total ratio approximation.
+  float hyp_SratioB_knowndenom(ColumnVector &xL,ColumnVector& denomvals){
+    float c,Norm1,t1,T1,R,K2,K3,K4,tmp,tmp2,tmp3;
+
+    //Approximate Numerator 
+    SortDescending(xL); //Not needed?
+    if (xL(1)==0 && xL(2)==0 && xL(3)==0){
+      Norm1=1; t1=0; T1=0; 
+    }
+    else{
+      t1=find_t(xL);
+      R=1; K2=0; K3=0; K4=0; 
+      for (int i=1; i<=3; i++){
+        tmp=xL(i)+t1;
+        tmp2=tmp*tmp; tmp3=tmp2*tmp;
+        R=-R*tmp;
+        K2=K2+0.5/tmp2;
+        K3=K3-1.0/tmp3;
+        K4=K4+3.0/(tmp3*tmp);
+      }
+      T1=K4/(8.0*K2*K2)-5.0*K3*K3/(24.0*K2*K2*K2);  
+      Norm1=K2*R;
+    }
+    //Final Ratio
+    if (denomvals(3)/Norm1>0)  
+      c=sqrt(denomvals(3)/Norm1)*exp(-t1+T1+denomvals(1)-denomvals(2));
+    else
+      c=0; //Signify that the approximation has failed. c should be >=0. How to treat these voxels???
+    if (isinf(c))  //In this case again the approximation has failed 
+      c=1e10;
+    return c;
+  }
+
+
+
+  //Saddlepoint approximation of the ratio of two hypergeometric functions, one with matrix argument L and another with scalar argument k. Vector xL contains the eigenvalues of L.
+  //Used for the ball & Watsons model, L has up to two non-zero eigenvalues and k!=0 is the fanning index.  
+  float hyp_SratioW(ColumnVector& xL,const double k){
+    float Norm1,t1,T1,R,K2,K3,K4,tmp,tmp2,tmp3,Rf, t2,T2, Norm2,Bm,c;
+
+    //Approximate Numerator 
+    if (xL(1)==0 && xL(2)==0){
+      Norm1=1; t1=0; T1=0; 
+    }
+    else{
+      SortDescending(xL);  //Not needed, performed by eigen-decomposition?
+      t1=find_t(xL);
+      R=1; K2=0; K3=0; K4=0; 
+      for (int i=1; i<=3; i++){
+	tmp=xL(i)+t1;
+	tmp2=tmp*tmp; tmp3=tmp2*tmp;
+        R=-R*tmp;
+        K2=K2+0.5/tmp2;
+        K3=K3-1.0/tmp3;
+        K4=K4+3.0/(tmp3*tmp);
+	}
+      T1=K4/(8.0*K2*K2)-5.0*K3*K3/(24.0*K2*K2*K2);  
+      Norm1=0.125/(K2*R);
+    }
+    
+    //Approximate Denominator
+    R=sqrt(4*k*k+9-4*k);
+    Rf=R+3+2*k;
+    t2=-0.25*Rf;
+    tmp=k+t2; tmp2=tmp*tmp; tmp3=tmp2*tmp;
+    Bm=1.0/Rf;
+    K2=0.5/tmp2+1.0/(t2*t2);
+    K3=-1.0/tmp3-2.0/(t2*t2*t2);
+    K4=3.0/(tmp3*tmp)+6.0/(t2*t2*t2*t2);
+    T2=K4/(8.0*K2*K2)-5.0*K3*K3/(24.0*K2*K2*K2);
+    Norm2=Bm/R;
+    
+    //Final Ratio
+    c=sqrt(Norm1/Norm2)*exp(-t1+T1+t2-T2);
+    return c;
+  }
+
+
+  //Saddlepoint aproximation of the ratio ot two hypergeometric functions, one with matrix arguments L and the other with scalar argument k>0 in two steps: 
+  //First denominator, then numerator. This allows them to be updated independently, used for the ball & Watsons model to compute the likelihood faster.
+  //This function returns values used in the denominator approximation.
+  ReturnMatrix approx_denominatorW(const double k){
+    float t2,R,Bm,Rf,K2,K3,K4,tmp,tmp2,tmp3, T2, Norm2;
+    ColumnVector Res(3);
+
+    R=sqrt(4*k*k+9-4*k);
+    Rf=R+3+2*k;
+    t2=-0.25*Rf;
+    tmp=k+t2; tmp2=tmp*tmp; tmp3=tmp2*tmp;
+    Bm=1.0/Rf;
+    K2=0.5/tmp2+1.0/(t2*t2);
+    K3=-1.0/tmp3-2.0/(t2*t2*t2);
+    K4=3.0/(tmp3*tmp)+6.0/(t2*t2*t2*t2);
+    T2=K4/(8.0*K2*K2)-5.0*K3*K3/(24.0*K2*K2*K2);
+    Norm2=Bm/R;
+    Res<< t2 << T2 << Norm2;
+
+    Res.Release();
+    return Res;
+  }
+
+
+  //Second step for saddlepoint approximation of the ratio of two hypergeometric functions, with matrix argument L and scalar argument k (xL has the eigenvalues of L). 
+  //Assume that the denominator has already been approximated by the function above and the parameters are stored in denomvals.
+  //Here approximate the numerator and return the total ratio approximation.
+  float hyp_SratioW_knowndenom(ColumnVector &xL,ColumnVector& denomvals){
+    float c,Norm1,t1,T1,R,K2,K3,K4,tmp,tmp2,tmp3;
+
+    //Approximate Numerator 
+    if (xL(1)==0 && xL(2)==0){
+      Norm1=1; t1=0; T1=0; 
+    }
+    else{
+      SortDescending(xL); 
+      t1=find_t(xL);
+      R=1; K2=0; K3=0; K4=0; 
+      for (int i=1; i<=3; i++){
+	tmp=xL(i)+t1;
+	tmp2=tmp*tmp; tmp3=tmp2*tmp;
+        R=-R*tmp;
+        K2=K2+0.5/tmp2;
+        K3=K3-1.0/tmp3;
+        K4=K4+3.0/(tmp3*tmp);
+	}
+      T1=K4/(8.0*K2*K2)-5.0*K3*K3/(24.0*K2*K2*K2);  
+      Norm1=0.125/(K2*R);
+    }
+    
+    //Final Ratio
+    c=sqrt(Norm1/denomvals(3))*exp(-t1+T1+denomvals(1)-denomvals(2));
+    return c;
+  }
+
+
+
+  //Using the values of vector x, construct a qubic equation and solve it analytically.
+  //Solution used for the saddlepoint approximation of the confluent hypergeometric function with matrix argument B (3x3) (See Kume & Wood, 2005)
+  //Vector x contains the eigenvalues of B.
+  float find_t(const ColumnVector& x){
+
+    float l1=-x(1), l2=-x(2), l3=-x(3);
+    float a0,a1,a2,a3,ee,tmp,z1,z2,z3,p,q,D,offset;
+
+    a3=l1*l2+l2*l3+l1*l3;
+    a2=1.5-l1-l2-l3;
+    a1=a3-l1-l2-l3;
+    a0=0.5*(a3-2*l1*l2*l3);
+
+    p=(a1-a2*a2*INV3)*INV3;
+    q=(-9*a2*a1+27*a0+2*a2*a2*a2)*INV54;
+    D=q*q+p*p*p;
+    offset=a2*INV3;
+    if (D>0){
+      ee=sqrt(D);
+      tmp=-q+ee; z1=croot(tmp);
+      tmp=-q-ee; z1=z1+croot(tmp);
+      z1=z1-offset; z2=z1; z3=z1;
+    }
+    else if (D<0){
+      ee=sqrt(-D);
+      float tmp2=-q; 
+      float angle=2*INV3*atan(ee/(sqrt(tmp2*tmp2+ee*ee)+tmp2));
+      tmp=cos(angle);
+      tmp2=sin(angle);
+      ee=sqrt(-p);
+      z1=2*ee*tmp-offset; 
+      z2=-ee*(tmp+SQRT3*tmp2)-offset; 
+      z3=-ee*(tmp-SQRT3*tmp2)-offset; 
+    }
+    else{
+      tmp=-q;
+      tmp=croot(tmp);
+      z1=2*tmp-offset; z2=z1; z3=z1;
+      if (p!=0 || q!=0)
+	z2=-tmp-offset; z3=z2;
+    }
+
+    z1=min3(z1,z2,z3);  //Pick the smallest root
+
+    //    if (z2<=l1)
+    // z1=z2;
+    //else if (z3<=l1)
+    //  z1=z3;
+    return z1;
+  }
+
+
+
+
+
+
diff --git a/Bingham_Watson_approx.h b/Bingham_Watson_approx.h
new file mode 100755
index 0000000..2602ee3
--- /dev/null
+++ b/Bingham_Watson_approx.h
@@ -0,0 +1,94 @@
+/*  Directional Statistics Functions
+
+    Bingham and Watson Distributions and functions to approximate their normalizing constant
+    
+    Stam Sotiropoulos  - FMRIB Image Analysis Group
+ 
+    Copyright (C) 2011 University of Oxford  */
+
+/*  CCOPYRIGHT  */
+
+#if !defined (Bingham_Watson_approx_h)
+#define Bingham_Watson_approx_h
+
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+#define WANT_STREAM
+#define WANT_MATH
+#include <string>
+#include "miscmaths/miscmaths.h"
+#include "miscmaths/nonlin.h"
+#include "stdlib.h"
+
+
+using namespace NEWMAT;
+using namespace MISCMATHS;
+
+
+  #ifndef M_PI
+  #define M_PI 3.14159265358979323846
+  #endif
+
+  #define INV3 0.333333333333333   // 1/3
+  #define SQRT3 1.732050807568877 //sqrt(3)
+  #define INV54 0.018518518518519 // 1/54
+ 
+  #define min3(a,b,c)  (a < b ? min(a,c) : min(b,c) )
+ 
+
+  //Saddlepoint approximation of confluent hypergeometric function of a matrix argument B
+  //Vector x has the eigenvalues of B.
+  float hyp_Sapprox(ColumnVector& x);
+
+ 
+  //Saddlepoint approximation of confluent hypergeometric function of a matrix argument, with its eigenvalues being l1,l1,l2 or l1,l2,l2 with l1!=l2.
+  //Vector x has the three eigenvalues. This function can be also used to approximate a confluent hypergeometric function of a scalar argument k
+  //by providing x=[k 0 0].
+  float hyp_Sapprox_twoequal(ColumnVector& x);
+
+ 
+  //Saddlepoint approximation of the ratio of two hypergeometric functions, with matrix arguments L and B (3x3). Vectors xL & xB contain the eigenvalues of L and B.
+  //Used for the ball & Binghams model. 
+  float hyp_SratioB(ColumnVector& xL,ColumnVector& xB);
+
+
+  //Saddlepoint aproximation of the ratio ot two hypergeometric functions with matrix arguments L and B in two steps: First denominator, then numerator. 
+  //This allows them to be updated independently, used for the ball & Binghams model to compute the likelihood faster.
+  //This function returns values used in the denominator approximation. xB containes the two non-zero eigenvalues of matrix B.
+  ReturnMatrix approx_denominatorB(ColumnVector& xB);
+
+
+  //Second step for saddlepoint approximation of the ratio of two hypergeometric functions with matrix arguments L and B (xL has the eigenvalues of L). 
+  //Assume that the denominator has already been approximated by the function above and the parameters are stored in denomvals.
+  //Here approximate the numerator and return the total ratio approximation.
+  float hyp_SratioB_knowndenom(ColumnVector &xL,ColumnVector& denomvals);
+
+
+  //Saddlepoint approximation of the ratio of two hypergeometric functions, one with matrix argument L and another with scalar argument k. Vector xL contains the eigenvalues of L.
+  //Used for the ball & Watsons model. 
+  float hyp_SratioW(ColumnVector& xL,const double k);
+
+
+  //Saddlepoint aproximation of the ratio ot two hypergeometric functions, one with matrix arguments L and the other with scalar argument k in two steps: 
+  //First denominator, then numerator. This allows them to be updated independently, used for the ball & Watsons model to compute the likelihood faster.
+  //This function returns values used in the denominator approximation.
+  ReturnMatrix approx_denominatorW(const double k);
+
+
+  //Second step for saddlepoint approximation of the ratio of two hypergeometric functions, with matrix argument L and scalar argument k (xL has the eigenvalues of L). 
+  //Assume that the denominator has already been approximated by the function above and the parameters are stored in denomvals.
+  //Here approximate the numerator and return the total ratio approximation.
+  float hyp_SratioW_knowndenom(ColumnVector &xL,ColumnVector& denomvals);
+
+
+  //Using the values of vector x, construct a qubic equation and solve it analytically.
+  //Solution used for the saddlepoint approximation of the confluent hypergeometric function with matrix argument B (3x3) (See Kume & Wood, 2005)
+  //Vector x contains the eigenvalues of B. 
+  float find_t(const ColumnVector& x);
+
+  //cubic root
+  float croot(const float& x);
+
+
+#endif
-- 
GitLab