From c08f6e831b8d8c31a0c74badb5493acb3ae17a41 Mon Sep 17 00:00:00 2001
From: Stamatios Sotiropoulos <stam@fmrib.ox.ac.uk>
Date: Fri, 14 May 2010 12:09:25 +0000
Subject: [PATCH] Added function to compute signal from isotropic compartment.
 Trigonmetric identities used to speed up likelihood calc

---
 fibre.h | 225 +++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 150 insertions(+), 75 deletions(-)

diff --git a/fibre.h b/fibre.h
index 93d0bd1..d62f45d 100644
--- a/fibre.h
+++ b/fibre.h
@@ -23,20 +23,23 @@ const float minlogfloat=-23;
 namespace FIBRE{
   
 
-  class Fibre{
-    float m_th;
+ ///////////////////////////////////////////////////////////////////////////////////////////// 
+ ////Class that represents one anisotropic compartment of the PVM model in an MCMC framework 
+ //////////////////////////////////////////////////////////////////////////////////////////// 
+ class Fibre{
+    float m_th;                   //Current/candidate MCMC state
     float m_ph;
     float m_f;
     float m_lam;
-    float m_th_prop;
+    float m_th_prop;              //Standard deviation for Gaussian proposal distributions of parameters
     float m_ph_prop;
     float m_f_prop;
     float m_lam_prop;
-    float m_th_old;
+    float m_th_old;               //Last accepted value. If a sample is rejected this value is restored
     float m_ph_old;
     float m_f_old;
     float m_lam_old;
-    float m_th_prior;
+    float m_th_prior;             //Priors for the model parameters 
     float m_ph_prior;
     float m_f_prior;
     float m_lam_prior;
@@ -44,10 +47,10 @@ namespace FIBRE{
     float m_ph_old_prior;
     float m_f_old_prior;
     float m_lam_old_prior;
-    float m_prior_en;
+    float m_prior_en;             //Joint Prior 
     float m_old_prior_en;
     float m_ardfudge;
-    int m_th_acc; 
+    int m_th_acc;     
     int m_th_rej;
     int m_ph_acc;
     int m_ph_rej; 
@@ -56,14 +59,14 @@ namespace FIBRE{
     int m_lam_acc; 
     int m_lam_rej;
     bool m_lam_jump;
-    ColumnVector m_Signal; 
+    ColumnVector m_Signal;        //Vector that stores the signal from the anisotropic compartment during the candidate/current MCMC state  
     ColumnVector m_Signal_old; 
     const float& m_d;
-    const float& m_d_std; //second d param in multi-shell model - std in gamma dist of diffusivities
-    const ColumnVector& m_alpha;
-    const ColumnVector& m_beta;
-    const Matrix& m_bvals;
-    const int& m_modelnum; 
+    const float& m_d_std;         //second d param in multi-shell model - std in gamma dist of diffusivities
+    const ColumnVector& m_alpha;  //Theta angles of bvecs 
+    const ColumnVector& m_beta;   //Phi angles of bvecs 
+    const Matrix& m_bvals;        //b values
+    const int& m_modelnum;        //1 for single-shell, 2 for multi-shell model 
  public:
     //constructors::
 
@@ -228,15 +231,17 @@ namespace FIBRE{
     inline float get_modelnum() const{ return m_modelnum;}
     
 
-    inline void update_proposals(){
+    //Adapt the standard deviation of the proposal distributions during MCMC execution 
+    //to avoid over-rejection/over-acceptance of samples
+    inline void update_proposals(){  
       m_th_prop*=sqrt(float(m_th_acc+1)/float(m_th_rej+1));
       m_th_prop=min(m_th_prop,maxfloat);
       m_ph_prop*=sqrt(float(m_ph_acc+1)/float(m_ph_rej+1));
       m_ph_prop=min(m_ph_prop,maxfloat);
       m_f_prop*=sqrt(float(m_f_acc+1)/float(m_f_rej+1));
       m_f_prop=min(m_f_prop,maxfloat);
-      m_lam_prop*=sqrt(float(m_lam_acc+1)/float(m_lam_rej+1));
-      m_lam_prop=min(m_lam_prop,maxfloat);
+      //m_lam_prop*=sqrt(float(m_lam_acc+1)/float(m_lam_rej+1));   //STAM: REmoved Update for m_lam_prior
+      //m_lam_prop=min(m_lam_prop,maxfloat);
       m_th_acc=0; 
       m_th_rej=0;
       m_ph_acc=0; 
@@ -256,11 +261,15 @@ namespace FIBRE{
       }
       return false; //instant rejection flag
     }
+
+
     inline bool compute_ph_prior(){
       m_ph_old_prior=m_ph_prior;
       m_ph_prior=0;
       return false;
     }
+
+
     inline bool compute_f_prior(bool can_use_ard=true){
       //note(gamma(lam+1)/(gamma(1)*gamma(lam)) = lam
       // the following is a beta distribution with alpha=0
@@ -273,7 +282,7 @@ namespace FIBRE{
 	  m_f_prior=0;
 	}
 	else{
-	  if(m_lam_jump){
+	  //if(m_lam_jump){              //STAM: Removed the if-else statement (always true since m_lam_jump=true)
 	    // m_f_prior=log(1-m_f)+2*log(fabs(log(1-m_f))); //marginalised with uniform prior on lambda
 	    m_f_prior=std::log(m_f);
 	    	  	
@@ -283,9 +292,9 @@ namespace FIBRE{
 	    //cc	  OUT(mmk);
 	    //cc	  OUT(m_f_old_prior);
 
-	  }
-	    else
-	    m_f_prior=0;
+	    //}
+	    //else
+	    //m_f_prior=0;
 	  
 	}
 	m_f_prior=m_ardfudge*m_f_prior;
@@ -293,7 +302,8 @@ namespace FIBRE{
 	return false;
       }
     }
-    
+
+     
     inline bool compute_lam_prior(){
       m_lam_old_prior=m_lam_prior;
       if(m_lam <0 | m_lam > 1e16 )
@@ -304,23 +314,31 @@ namespace FIBRE{
       }
     }
     
+
     inline void compute_prior(){
       m_old_prior_en=m_prior_en;
-      m_prior_en=m_th_prior+m_ph_prior+m_f_prior+m_lam_prior;
+      //m_prior_en=m_th_prior+m_ph_prior+m_f_prior+m_lam_prior;   //STAM:Removed m_lam_prior
+      m_prior_en=m_th_prior+m_ph_prior+m_f_prior;
     }
 
-     void compute_signal(){
+
+    //Compute model predicted signal, only due to the anisotropic compartment 
+    void compute_signal(){
        m_Signal_old=m_Signal;
        for (int i = 1; i <= m_alpha.Nrows(); i++){
- 	float angtmp=cos(m_ph-m_beta(i))*sin(m_alpha(i))*sin(m_th) + cos(m_alpha(i))*cos(m_th);
+	 float cos_alpha_minus_theta=cos(m_alpha(i)-m_th);   //STAM: Used trigonometric identity to reduce the number of sinusoids evaluated  
+         float cos_alpha_plus_theta=cos(m_alpha(i)+m_th);
+          
+	//float angtmp=cos(m_ph-m_beta(i))*sin(m_alpha(i))*sin(m_th) + cos(m_alpha(i))*cos(m_th);
+	float angtmp=cos(m_ph-m_beta(i))*(cos_alpha_minus_theta-cos_alpha_plus_theta)/2 + (cos_alpha_minus_theta+cos_alpha_plus_theta)/2;
  	angtmp=angtmp*angtmp;
 	if(m_modelnum==1){
 	  m_Signal(i)=exp(-m_d*m_bvals(1,i)*angtmp);
 	  //	  cout<<"here"<<endl;
 	}
 	else if(m_modelnum==2){
-	  float dalpha=m_d*m_d/(m_d_std*m_d_std);
 	  float dbeta=m_d/(m_d_std*m_d_std);
+	  float dalpha=m_d*dbeta;                            //STAM: Removed one division
 	  m_Signal(i)=exp( log(dbeta/(dbeta + m_bvals(1,i)*angtmp)) *dalpha ); //gamma distribution of diffusivities - params alpha (m_d) and beta (m_d_beta): Expected signal is (beta./(beta+b*g(th,ph))).^alpha;
 	  //cout<<i<<" "<<angtmp<<" "<<m_d_std<<" "<<m_d<<endl;
 	  //cout<<"balh "<<m_Signal(i)<<endl;
@@ -341,10 +359,12 @@ namespace FIBRE{
       return rejflag;
     };
 
+
     inline void accept_th(){
       m_th_acc++;      
     }
     
+
     inline void reject_th(){
       m_th=m_th_old;
       m_th_prior=m_th_old_prior;
@@ -353,6 +373,7 @@ namespace FIBRE{
       m_th_rej++;
     }
     
+
     inline bool propose_ph(){
       //cout<<"ph"<<endl;
       m_ph_old=m_ph;
@@ -363,10 +384,12 @@ namespace FIBRE{
       return rejflag;
     };
     
+
     inline void accept_ph(){
       m_ph_acc++;
     }
     
+
     inline void reject_ph(){
       m_ph=m_ph_old;
       m_ph_prior=m_ph_old_prior;
@@ -388,10 +411,14 @@ namespace FIBRE{
 
       return rejflag_th | rejflag_ph;
     }
+
+
     void accept_th_ph(){
       m_th_acc++;
       m_ph_acc++;
     }
+
+
     void reject_th_ph(){
       m_ph=m_ph_old;
       m_ph_prior=m_ph_old_prior;
@@ -406,6 +433,7 @@ namespace FIBRE{
       m_ph_rej++;
     }
 
+
     inline bool propose_f( bool can_use_ard=true){
       //cout<<"f"<<endl;
       m_f_old=m_f;
@@ -415,9 +443,11 @@ namespace FIBRE{
       return rejflag;
     };
     
+
     inline void accept_f(){
       m_f_acc++;
     }
+
     
     inline void reject_f(){
       m_f=m_f_old;
@@ -425,6 +455,7 @@ namespace FIBRE{
       m_prior_en=m_old_prior_en;
       m_f_rej++;
     }
+
     
     inline bool propose_lam(){
       //cout<<"lam"<<endl;
@@ -438,10 +469,12 @@ namespace FIBRE{
       }
       else {return true;}
     };
+
     
     inline void accept_lam(){
       m_lam_acc++;
     }
+
     
     inline void reject_lam(){
       m_lam=m_lam_old;
@@ -492,6 +525,7 @@ namespace FIBRE{
     friend  ostream& operator<<(ostream& ostr,const Fibre& p);
 
   };
+
 //overload <<
   inline ostream& operator<<(ostream& ostr,const Fibre& p){
     ostr<<p.m_th<<" "<<p.m_ph<<" "<<p.m_f<<endl;
@@ -499,6 +533,11 @@ namespace FIBRE{
   }
 
 
+  ///////////////////////////////////////////////////////////////////////////////////////////////////// 
+  //Class that represents the PVM model in an MCMC framework. An isotropic compartment and M>=1 
+  //anisotropic compartments (Fibre objects) are considered. Functions are defined for performing 
+  //a single MCMC iteration   
+  ///////////////////////////////////////////////////////////////////////////////////////////////////// 
   class Multifibre{
     vector<Fibre> m_fibres;
     float m_d;
@@ -522,19 +561,21 @@ namespace FIBRE{
     float m_S0_old_prior;
     float m_S0_acc;
     float m_S0_rej;
-    float m_prior_en;
+    float m_prior_en;             //Joint Prior
     float m_old_prior_en;
-    float m_likelihood_en;
+    float m_likelihood_en;        //Likelihood
     float m_old_likelihood_en;
-    float m_energy;
+    float m_energy;               //Posterior
     float m_old_energy;
     float m_ardfudge;
+    ColumnVector m_iso_Signal;    //STAM:Vector that stores the signal from the isotropic compartment during the candidate/current state 
+    ColumnVector m_iso_Signal_old; 
 
-    const ColumnVector& m_data;
-    const ColumnVector& m_alpha;
-    const ColumnVector& m_beta;
-    const Matrix& m_bvals;
-    const int& m_modelnum;
+    const ColumnVector& m_data;   //Data vector 
+    const ColumnVector& m_alpha;  //Theta angles of bvecs 
+    const ColumnVector& m_beta;   //Phi angles of bvecs
+    const Matrix& m_bvals;        //b values
+    const int& m_modelnum;        //1 for single-shell, 2 for multi-shell model
     
 
   public:
@@ -544,7 +585,9 @@ namespace FIBRE{
 	       const ColumnVector& beta, const Matrix& b, int N ,float ardfudge=1, int modelnum=1):
       m_ardfudge(ardfudge),m_data(data), m_alpha(alpha), m_beta(beta), m_bvals(b), m_modelnum(modelnum){
       //      initialise(Amat,N);
-
+      m_iso_Signal.ReSize(alpha.Nrows());
+      m_iso_Signal=0;
+      m_iso_Signal_old=m_iso_Signal;            //STAM: Initialize vectors that keep the signal from the isotropic compartment
     }
     
     ~Multifibre(){}
@@ -552,13 +595,14 @@ namespace FIBRE{
     const vector<Fibre>& fibres() const{
       return m_fibres;
     }
+
     vector<Fibre>& get_fibres(){
       return m_fibres;
     }
     
     void addfibre(const float th, const float ph, const float f,const float lam, const bool lam_jump=true){
       Fibre fib(m_alpha,m_beta,m_bvals,m_d,m_ardfudge,th,ph,f,lam,lam_jump,m_modelnum,m_d_std);
-      m_fibres.push_back(fib);
+       m_fibres.push_back(fib);
     }
 
     void addfibre(){
@@ -574,16 +618,20 @@ namespace FIBRE{
       m_prior_en=0;
       compute_prior();
       m_likelihood_en=0;
+      compute_iso_signal();                  //STAM:Initialize the isotropic compartment signal before getting the likelihood for the first time
       compute_likelihood();
       compute_energy();
     }
-    
-    void initialise_props(){
-      m_S0_prop=m_S0/10; //must have set inital values before this is called;
-      m_d_prop=m_d/10;
-      m_d_std_prop=m_d_std/10;
+
+
+    //Initialize standard deviations for the proposal distributions
+    void initialise_props(){   
+      m_S0_prop=m_S0/10.0; //must have set inital values before this is called;
+      m_d_prop=m_d/10.0;
+      m_d_std_prop=m_d_std/10.0;
     }
     
+
     inline float get_d() const{ return m_d;}
     inline void set_d(const float d){ m_d=d; }
 
@@ -661,6 +709,8 @@ namespace FIBRE{
 	}
     }
 
+
+    //Check if sum of volume fractions is >1
     bool reject_f_sum(){
       float fsum=0;
       for(unsigned int f=0;f<m_fibres.size();f++){
@@ -669,6 +719,8 @@ namespace FIBRE{
       return fsum>1; //true if sum(f) > 1 and therefore, we should reject f
     }
     
+
+    //Compute Joint Prior
     inline void compute_prior(){
       m_old_prior_en=m_prior_en;
       m_prior_en=m_d_prior+m_S0_prior;
@@ -679,7 +731,23 @@ namespace FIBRE{
       } 
     }
     
-    void compute_likelihood(){
+
+    void compute_iso_signal(){               //STAM: Function that computes the signal from the isotropic compartment 
+      m_iso_Signal_old=m_iso_Signal;
+      for(int i=1;i<=m_alpha.Nrows();i++)
+	{
+	if(m_modelnum==1)
+	  m_iso_Signal(i)=exp(-m_d*m_bvals(1,i));
+	else if(m_modelnum==2)
+	  {
+	  float dbeta=m_d/(m_d_std*m_d_std);
+	  float dalpha=m_d*dbeta;	  
+	  m_iso_Signal(i)=exp(log(dbeta/(dbeta+m_bvals(1,i)))*dalpha);
+	  }
+	}
+    }	
+    
+   void compute_likelihood(){
 
       m_old_likelihood_en=m_likelihood_en;
       ColumnVector pred(m_alpha.Nrows());
@@ -690,63 +758,59 @@ namespace FIBRE{
 	fsum+=m_fibres[f].get_f();
       }
 
-      for(int i=1;i<=pred.Nrows();i++){
-	if(m_modelnum==1)
-	  pred(i)=pred(i)+(1-fsum)*exp(-m_d*m_bvals(1,i));
-	else if(m_modelnum==2){
-	  float dalpha=m_d*m_d/(m_d_std*m_d_std);
-	  float dbeta=m_d/(m_d_std*m_d_std);
-	  pred(i)=pred(i)+(1-fsum)*exp(log(dbeta/(dbeta+m_bvals(1,i)))*dalpha);
-	}
-      }
+      for(int i=1;i<=pred.Nrows();i++)
+	pred(i)=pred(i)+(1-fsum)*m_iso_Signal(i);      //STAM: Use the precalculated isotropic compartment 
       pred=pred*m_S0;
       
       float sumsquares=(m_data-pred).SumSquare();
-      m_likelihood_en=(m_data.Nrows()/2)*log(sumsquares/2);
+      m_likelihood_en=(m_data.Nrows()/2.0)*log(sumsquares/2.0);
       
     }
     
-  
+    
     inline  void  compute_energy(){
       m_old_energy=m_energy;
       m_energy=m_prior_en+m_likelihood_en;
     }
-    
+   
+ 
     void evaluate_energy(){
       m_old_energy=m_energy;
-
       for(unsigned int f=0;f<m_fibres.size();f++){
 	m_fibres[f].compute_th_prior();
 	m_fibres[f].compute_ph_prior();
 	m_fibres[f].compute_f_prior();
-	m_fibres[f].compute_lam_prior();
+	//m_fibres[f].compute_lam_prior();     //STAM: Removed the lam_prior calulation
 	m_fibres[f].compute_signal();
 	m_fibres[f].compute_prior();
       }
       compute_prior();
       compute_likelihood();
-
       m_energy=m_prior_en+m_likelihood_en;
     }
 
 
+    //Test whether the candidate state will be accepted 
     bool test_energy(){
       float tmp=exp(m_old_energy-m_energy);
       //cout<<m_old_energy<< " " << m_energy <<endl;
       return (tmp>unifrnd().AsScalar());
     }
+
     
     inline void restore_energy(){
       m_prior_en=m_old_prior_en;
       m_likelihood_en=m_old_likelihood_en;
       m_energy=m_old_energy;
     }
+
     
     inline void restore_energy_no_lik(){
       m_prior_en=m_old_prior_en;
       m_energy=m_old_energy;
     }
 
+
     inline bool propose_d(){
       //cout<<"d"<<endl;
       m_d_old=m_d;
@@ -754,12 +818,15 @@ namespace FIBRE{
       bool rejflag=compute_d_prior();//inside this it stores the old prior
       for(unsigned int f=0;f<m_fibres.size();f++)
 	m_fibres[f].compute_signal();
+      compute_iso_signal();          //STAM: Compute the signal from the isotropic compartment 
       return rejflag;
     };
     
+
     inline void accept_d(){
       m_d_acc++;      
     }
+
     
     inline void reject_d(){
       m_d=m_d_old;
@@ -767,8 +834,11 @@ namespace FIBRE{
       m_prior_en=m_old_prior_en;
       for(unsigned int f=0;f<m_fibres.size();f++)
 	m_fibres[f].restoreSignal();
+      m_iso_Signal=m_iso_Signal_old;   //STAM:Restore the signal from the isotropic compartment
       m_d_rej++;
     }
+
+
     inline bool propose_d_std(){
       //cout<<"d_std"<<endl;
       m_d_std_old=m_d_std;
@@ -776,22 +846,27 @@ namespace FIBRE{
       bool rejflag=compute_d_std_prior();//inside this it stores the old prior
       for(unsigned int f=0;f<m_fibres.size();f++)
 	m_fibres[f].compute_signal();
+      compute_iso_signal();   //STAM: Compute the signal from the isotropic compartment
       return rejflag;
     };
     
+
     inline void accept_d_std(){
       m_d_std_acc++;      
     }
     
+
     inline void reject_d_std(){      
       m_d_std=m_d_std_old;
       m_d_std_prior=m_d_std_old_prior;
       m_prior_en=m_old_prior_en;
       for(unsigned int f=0;f<m_fibres.size();f++)
 	m_fibres[f].restoreSignal();
+      m_iso_Signal=m_iso_Signal_old;    //STAM:Restore the signal from the isotropic compartment
       m_d_std_rej++;
     }
     
+
     inline bool propose_S0(){
       //cout<<"S0"<<endl;
       m_S0_old=m_S0;
@@ -800,10 +875,12 @@ namespace FIBRE{
       return rejflag;
     };
     
+
     inline void accept_S0(){
       m_S0_acc++;      
     }
     
+
     inline void reject_S0(){
       m_S0=m_S0_old;
       m_S0_prior=m_S0_old_prior;
@@ -811,6 +888,9 @@ namespace FIBRE{
       m_S0_rej++;
     }
     
+
+    //Adapt standard deviation of proposal distributions during MCMC execution 
+    //to avoid over-rejection/over-acceptance of MCMC samples
     inline void update_proposals(){
       m_d_prop*=sqrt(float(m_d_acc+1)/float(m_d_rej+1));
       m_d_prop=min(m_d_prop,maxfloat);
@@ -831,16 +911,14 @@ namespace FIBRE{
       }
       
     }
-
-    
     
     
-
+    //Function that performs a single MCMC iteration
     void jump( bool can_use_ard=true ){
       //       cout<<"modelnum "<<m_modelnum<<endl;
       //       cout<<"fmodelnum "<<m_fibres[0].get_modelnum()<<endl;
       //       cout<<"fmodelnum2 "<<m_fibres[1].get_modelnum()<<endl;
-      if(!propose_d()){
+      if(!propose_d()){          //Try d 
 	compute_prior();
 	compute_likelihood();
 	compute_energy();
@@ -856,7 +934,7 @@ namespace FIBRE{
 	reject_d();
       }
       
-      if(m_modelnum==2){
+      if(m_modelnum==2){     //Try d_std
 	if(!propose_d_std()){
 	  compute_prior();
 	  compute_likelihood();
@@ -873,7 +951,7 @@ namespace FIBRE{
 	  reject_d_std();
 	}
       }
-      if(!propose_S0()){
+      if(!propose_S0()){    //Try S0
 	compute_prior();
 	compute_likelihood();
 	compute_energy();
@@ -888,14 +966,14 @@ namespace FIBRE{
 	reject_S0();
       } 
       
-      for(unsigned int f=0;f<m_fibres.size();f++){
+      for(unsigned int f=0;f<m_fibres.size();f++){  //For each fibre
 	//cout<<"pre th "<<m_fibres[f].get_th()<<endl;	
 	//cout << m_fibres[f].getSignal()<<endl;
 	//cout<<"prior"<<m_prior_en<<endl;
 	//cout<<"lik"<<m_likelihood_en<<endl;
 	//cout<<"energy"<<m_energy<<endl;
 	//cout <<endl;
-	if(!m_fibres[f].propose_th()){
+	if(!m_fibres[f].propose_th()){   //Try theta
 	  
 	  compute_prior();
 	  compute_likelihood();
@@ -923,7 +1001,7 @@ namespace FIBRE{
 	  m_fibres[f].reject_th();
 	}
 	
-	  if(!m_fibres[f].propose_ph()){
+	if(!m_fibres[f].propose_ph()){  //Try phi
 	    compute_prior();
 	    compute_likelihood();
 	    compute_energy();
@@ -938,7 +1016,7 @@ namespace FIBRE{
 	    m_fibres[f].reject_ph();
 	  }
 	  
-	  if(!m_fibres[f].propose_f( can_use_ard )){
+	if(!m_fibres[f].propose_f( can_use_ard )){  //Try f 
 	    if(!reject_f_sum()){
 	      compute_prior();
 	      compute_likelihood();
@@ -959,7 +1037,6 @@ namespace FIBRE{
 	    m_fibres[f].reject_f();
 	  }
 	  
-	  
 //	   if(!m_fibres[f].propose_lam()){
 // 	    compute_prior();
 // 	    compute_energy();
@@ -974,13 +1051,10 @@ namespace FIBRE{
 // 	  else{
 // 	    m_fibres[f].reject_lam();
 // 	  }
-      
- 
       }
-      
-      
-      
     }
+
+
     Multifibre& operator=(const Multifibre& rhs){
       m_fibres=rhs.m_fibres;
       m_d=rhs.m_d;
@@ -1011,13 +1085,14 @@ namespace FIBRE{
       m_energy=rhs.m_energy;
       m_old_energy=rhs.m_old_energy;
       m_ardfudge=rhs.m_ardfudge;
-      
+      m_iso_Signal=rhs.m_iso_Signal;              //STAM: Equate the Iso_signal vectors as well
+      m_iso_Signal_old=rhs.m_iso_Signal_old;
       return *this;
     }
-    
+ 
+   
   };
   
-  
 }
 
 #endif
-- 
GitLab