package statalign.model.ext.plugins.structalign;
import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.apache.commons.math3.linear.ArrayRealVector;
import org.apache.commons.math3.linear.RealVector;
import statalign.base.Utils;
import statalign.utils.BetaDistribution;
public class vonMisesFisher{
/**
*
* @param kappa - concentration parameter
* @param mean - mean direction
* @param v - vector to be evaluated
* @return log density of distribution at @param v
*/
static double logDensity(double kappa, Vector3D mean, Vector3D v){
return Math.log(kappa) - Math.log(4 * Math.PI * Math.sinh(kappa)) + kappa * mean.dotProduct(v);
}
static double density(double kappa, Vector3D mean, Vector3D v){
return kappa / (4 * Math.PI * Math.sinh(kappa)) * Math.exp(kappa * mean.dotProduct(v));
}
/**
*
* @param kappa - concentration parameter
* @param v - mean vector
* @return - vector simulated from von Mises-Fisher distribution with given parameters
*/
// TODO replace with exact simulation method for S2 from Kent et al 2012
static Vector3D simulate(double kappa, Vector3D mean){
double b, x0, c, z, u, w;
int dim = 3;
RealVector unitSphere = new ArrayRealVector(new double[dim-1]);
RealVector sample;
do {
b = (-2*kappa + Math.pow(Math.pow(4*Math.pow(kappa,2) + (dim-1),2),.5) ) / (dim-1);
x0 = (1-b)/(1+b);
c = kappa*x0 + (dim-1)*Math.log(1-Math.pow(x0, 2));
z = new BetaDistribution((dim-1)/2,(dim-1)/2).sample();
u = Utils.generator.nextDouble();
w = (1 - (1+b)*z)/(1 - (1-b)*z);
} while(kappa*w + (dim-1) * Math.log(1-x0*w) - c < Math.log(u));
for(int i = 0; i < dim - 1; i++)
unitSphere.setEntry(i, Utils.generator.nextGaussian());
unitSphere = unitSphere.unitVector();
sample = unitSphere.mapMultiply(Math.pow(1-Math.pow(w,2), .5));
Vector3D sample3D = new Vector3D(sample.getEntry(0), sample.getEntry(1), w);
// calculate rotation to rotate (0,0,1) to mean
Rotation Q = new Rotation(new Vector3D(0, 0, 1), mean);
// Output simulated vector rotated to center around mean
sample3D = Q.applyTo(sample3D);
return sample3D;
}
// </vonMisesFisher>
}