-
Stamatios Sotiropoulos authoredStamatios Sotiropoulos authored
Bingham_Watson_approx.cc 10.10 KiB
/* 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;
}