Skip to content
Snippets Groups Projects
fibre.h 33.11 KiB
/*  Copyright (C) 2005 University of Oxford  */

/*  CCOPYRIGHT  */

#ifndef __FIBRE_H_
#define __FIBRE_H_


#include <iostream>
#include "stdlib.h"
#include "libprob.h"
#include <cmath>
#include "miscmaths/miscprob.h"

using namespace std; 
using namespace NEWMAT;
using namespace MISCMATHS;

const float maxfloat=1e10;
const float minfloat=1e-10;
const float maxlogfloat=23;
const float minlogfloat=-23;
namespace FIBRE{
  

  //Returns the natural log of the 0th order modified Bessel function of first kind for an argument x
  //Follows the exponential implementation of the Bessel function in Numerical Recipes, Ch. 6
  float logIo(const float& x){
    float y,b;

    b=std::fabs(x);
    if (b<3.75){
      float a=x/3.75;
      a*=a;
      //Bessel function evaluation
      y=1.0+a*(3.5156229+a*(3.0899424+a*(1.2067492+a*(0.2659732+a*(0.0360768+a*0.0045813)))));
      y=std::log(y);
    }
    else{
      float a=3.75/b; 
      //Bessel function evaluation
      //y=(exp(b)/sqrt(b))*(0.39894228+a*(0.01328592+a*(0.00225319+a*(-0.00157565+a*(0.00916281+a*(-0.02057706+a*(0.02635537+a*(-0.01647633+a*0.00392377))))))));
      //Logarithm of Bessel function
      y=b+std::log((0.39894228+a*(0.01328592+a*(0.00225319+a*(-0.00157565+a*(0.00916281+a*(-0.02057706+a*(0.02635537+a*(-0.01647633+a*0.00392377))))))))/std::sqrt(b));
    }

    return y;
  }



 ///////////////////////////////////////////////////////////////////////////////////////////// 
 ////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;              //Standard deviation for Gaussian proposal distributions of parameters
    float m_ph_prop;
    float m_f_prop;
    float m_lam_prop;
    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;             //Priors for the model parameters 
    float m_ph_prior;
    float m_f_prior;
    float m_lam_prior;
    float m_th_old_prior;
    float m_ph_old_prior;
    float m_f_old_prior;
    float m_lam_old_prior;
    float m_prior_en;             //Joint Prior 
    float m_old_prior_en;
    float m_ardfudge;
    int m_th_acc;     
    int m_th_rej;
    int m_ph_acc;
    int m_ph_rej; 
    int m_f_acc;
    int m_f_rej;
    int m_lam_acc; 
    int m_lam_rej;
    bool m_lam_jump;
    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;  //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::

    Fibre( const ColumnVector& alpha, const ColumnVector& beta, 
	   const Matrix& bvals,const float& d,const float& ardfudge=1,const int& modelnum=1, const float& d_std=0.01):
      m_ardfudge(ardfudge), m_d(d),m_d_std(d_std), m_alpha(alpha), m_beta(beta), m_bvals(bvals), m_modelnum(modelnum){
	m_th=M_PI/2;
	m_th_old=m_th;
	m_ph=0;
	m_ph_old=m_ph;
	m_f=0.01;
	m_f_old=m_f;
	m_lam=10;
	m_lam_old=m_lam;
	m_lam_jump=true;
	m_th_prop=0.2;
	m_ph_prop=0.2;
	m_f_prop=0.2;
	m_lam_prop=1;

	m_th_prior=0;
	compute_th_prior();
	
	m_ph_prior=0;
	compute_ph_prior();
	
	m_f_prior=0;
	compute_f_prior();
	//cc	OUT(m_f_prior);
	//cc	OUT(m_ardfudge);
	m_lam_prior=0;
	compute_lam_prior();

      m_Signal.ReSize(alpha.Nrows());
      m_Signal=0;
      m_Signal_old=m_Signal;

      compute_prior();
      compute_signal();

      m_th_acc=0; m_th_rej=0;
      m_ph_acc=0; m_ph_rej=0;
      m_f_acc=0; m_f_rej=0;
      m_lam_acc=0; m_lam_rej=0;
    }
    Fibre(const ColumnVector& alpha, 
	  const ColumnVector& beta, const Matrix& bvals, const float& d, const float& ardfudge,
	  const float& th, const float& ph, const float& f, 
	  const bool lam_jump=true, const int& modelnum=1, const float& d_std=0.01) : 
      m_th(th), m_ph(ph), m_f(f), m_ardfudge(ardfudge),m_lam_jump(lam_jump),  m_d(d), m_d_std(d_std), m_alpha(alpha), m_beta(beta), m_bvals(bvals), m_modelnum(modelnum)
    {
      
      m_th_old=m_th;
      m_ph_old=m_ph;
      m_f_old=m_f;
      m_lam_old=m_lam;
      m_th_prop=0.2;
      m_ph_prop=0.2;
      m_f_prop=0.2;
      m_lam_prop=1;
      
      m_th_prior=0;
      compute_th_prior();
      
      m_ph_prior=0;
      compute_ph_prior();
      
      m_f_prior=0;
      compute_f_prior();
      m_lam_prior=0;
      compute_lam_prior();
      

      m_Signal.ReSize(alpha.Nrows());
      m_Signal=0;
      m_Signal_old=m_Signal;

      compute_prior();
      compute_signal();
	    
      m_th_acc=0; m_th_rej=0;
      m_ph_acc=0; m_ph_rej=0;
      m_f_acc=0; m_f_rej=0;
      m_lam_acc=0; m_lam_rej=0;
     
    }
    Fibre(const Fibre& rhs): 
      m_d(rhs.m_d), m_d_std(rhs.m_d_std), m_alpha(rhs.m_alpha), m_beta(rhs.m_beta), m_bvals(rhs.m_bvals), m_modelnum(rhs.m_modelnum){
      *this=rhs;
    }

      
    ~Fibre(){}
    
    inline float get_th() const{ return m_th;}
    inline void set_th(const float th){ m_th=th; }
    
    inline float get_ph() const{ return m_ph;}
    inline void set_ph(const float ph){ m_ph=ph; }
    
    inline float get_f() const{ return m_f;}
    inline void set_f(const float f){ m_f=f; }
    
    inline float get_lam() const{ return m_lam;}
    inline void set_lam(const float lam){ m_lam=lam; }
    
    inline void report() const {
    OUT(m_th);
    OUT(m_ph);
    OUT(m_f);
    OUT(m_lam);
    OUT(m_th_prop);
    OUT(m_ph_prop);
    OUT(m_f_prop);
    OUT(m_lam_prop);
    OUT(m_th_old);
    OUT(m_ph_old);
    OUT(m_f_old);
    OUT(m_lam_old);
    OUT(m_th_prior);
    OUT(m_ph_prior);
    OUT(m_f_prior);
    OUT(m_lam_prior);
    OUT(m_th_old_prior);
    OUT(m_ph_old_prior);
    OUT(m_f_old_prior);
    OUT(m_lam_old_prior);
    OUT(m_prior_en);
    OUT(m_old_prior_en);
    OUT(m_th_acc); 
    OUT(m_th_rej);
    OUT(m_ph_acc);
    OUT(m_ph_rej); 
    OUT(m_f_acc);
    OUT(m_f_rej);
    OUT(m_lam_acc); 
    OUT(m_lam_rej);
    OUT(m_lam_jump);
      

    }
    
    inline const ColumnVector& getSignal() const{  
      return m_Signal;                      
    }
    
    inline void restoreSignal() {
      m_Signal=m_Signal_old;
    }
    inline void setSignal(const ColumnVector& Signal){
      m_Signal=Signal;
    }
    
    inline void setSignal(const int i, const float val){
      m_Signal(i)=val;
    }

    inline float get_prior() const{ return m_prior_en;}
    inline float get_modelnum() const{ return m_modelnum;}
    

    //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_th_acc=0; 
      m_th_rej=0;
      m_ph_acc=0; 
      m_ph_rej=0;
      m_f_acc=0; 
      m_f_rej=0;
      m_lam_acc=0; 
      m_lam_rej=0;
    }
    
    
    inline bool compute_th_prior(){
      m_th_old_prior=m_th_prior;
      if(m_th==0){m_th_prior=0;}
      else{
	m_th_prior=-log(fabs(sin(m_th)/2));
      }
      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
      m_f_old_prior=m_f_prior;
      if ((m_f<=0) | (m_f>=1))
	return true;
      else{
	//m_f_prior=-(log(m_lam) + (m_lam-1)*log(1-m_f));
	if(!can_use_ard)
	  m_f_prior=0;
	else{
	  if(m_lam_jump){              
	    // 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);
	    //cc	  OUT(m_f);
	    //cc	  OUT(m_ardfudge);
	    //cc	  float mmk=m_ardfudge*m_f_prior;
	    //cc	  OUT(mmk);
	    //cc	  OUT(m_f_old_prior);
	  }
	  else
	    m_f_prior=0;
	}
	m_f_prior=m_ardfudge*m_f_prior;
	return false;
      }
    }

     
    inline bool compute_lam_prior(){
      m_lam_old_prior=m_lam_prior;
      if((m_lam <0) | (m_lam > 1e16))
	return true;
      else{
	m_lam_prior=0;
	return false;
      }
    }
    

    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;
    }


    //Compute model predicted signal, only due to the anisotropic compartment 
    void compute_signal(){
       m_Signal_old=m_Signal;
       
       if(m_modelnum==1 || m_d_std<1e-5){
	 for (int i = 1; i <= m_alpha.Nrows(); i++){
	   float cos_alpha_minus_theta=cos(m_alpha(i)-m_th);   //Used trigonometric identities 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;	  
	  m_Signal(i)=exp(-m_d*m_bvals(1,i)*angtmp);
	 }
       }
       else if(m_modelnum==2){
	 float dbeta=m_d/(m_d_std*m_d_std);
	 float dalpha=m_d*dbeta;                            
	 for (int i = 1; i <= m_alpha.Nrows(); i++){
	   float cos_alpha_minus_theta=cos(m_alpha(i)-m_th);   
           float cos_alpha_plus_theta=cos(m_alpha(i)+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;
           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;
 	 }
       }
    }


    inline bool propose_th(){
      //cout<<"th"<<endl;
      m_th_old=m_th;
      m_th+=normrnd().AsScalar()*m_th_prop;
      bool rejflag=compute_th_prior();//inside this it stores the old prior
      compute_prior();
      compute_signal();
      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;
      m_prior_en=m_old_prior_en;
      m_Signal=m_Signal_old;//Is there a better way of doing this??
      m_th_rej++;
    }
    

    inline bool propose_ph(){
      //cout<<"ph"<<endl;
      m_ph_old=m_ph;
      m_ph+=normrnd().AsScalar()*m_ph_prop;
       bool rejflag=compute_ph_prior();//inside this it stores the old prior
      compute_prior();
      compute_signal();
      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;
      m_prior_en=m_old_prior_en;
      m_Signal=m_Signal_old;//Is there a better way of doing this??
      m_ph_rej++;
    }
    
    
    bool propose_th_ph(float th,float ph){
      //m_th_old=m_th;m_ph_old=m_ph;
      //cout<<"thph"<<endl;
      m_th=th;m_ph=ph;
      bool rejflag_th=compute_th_prior();//inside this it stores the old prior
      bool rejflag_ph=compute_ph_prior();

      compute_prior();
      compute_signal();

      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;

      m_th=m_th_old;
      m_th_prior=m_th_old_prior;

      m_prior_en=m_old_prior_en;
      m_Signal=m_Signal_old;//Is there a better way of doing this??

      m_th_rej++;
      m_ph_rej++;
    }


    inline bool propose_f( bool can_use_ard=true){
      m_f_old=m_f;
      m_f+=normrnd().AsScalar()*m_f_prop;
      bool rejflag=compute_f_prior(can_use_ard);
      compute_prior();
      return rejflag;
    };
    

    inline void accept_f(){
      m_f_acc++;
    }

    
    inline void reject_f(){
      m_f=m_f_old;
      m_f_prior=m_f_old_prior;
      m_prior_en=m_old_prior_en;
      m_f_rej++;
    }

    
    inline bool propose_lam(){
      if(m_lam_jump){
	m_lam_old=m_lam;
	m_lam+=normrnd().AsScalar()*m_lam_prop;
	bool rejflag=compute_lam_prior();
	compute_f_prior();
	compute_prior();
	return rejflag;
      }
      else {return true;}
    };

    
    inline void accept_lam(){
      m_lam_acc++;
    }

    
    inline void reject_lam(){
      m_lam=m_lam_old;
      m_lam_prior=m_lam_old_prior;
      m_prior_en=m_old_prior_en;
      m_lam_rej++;
    }
    
    
    Fibre& operator=(const Fibre& rhs){
      m_th=rhs.m_th;
      m_ph=rhs.m_ph;
      m_f=rhs.m_f;
      m_lam=rhs.m_lam;
      m_th_prop=rhs.m_th_prop;
      m_ph_prop=rhs.m_ph_prop;
      m_f_prop=rhs.m_f_prop;
      m_lam_prop=rhs.m_lam_prop;
      m_th_old=rhs.m_th_old;
      m_ph_old=rhs.m_ph_old;
      m_f_old=rhs.m_f_old;
      m_lam_old=rhs.m_lam_old;
      m_th_prior=rhs.m_th_prior;
      m_ph_prior=rhs.m_ph_prior;
      m_f_prior=rhs.m_f_prior;
      m_lam_prior=rhs.m_lam_prior;
      m_th_old_prior=rhs.m_th_old_prior;
      m_ph_old_prior=rhs.m_ph_old_prior;
      m_f_old_prior=rhs.m_f_old_prior;
      m_lam_old_prior=rhs.m_lam_old_prior;
      m_prior_en=rhs.m_prior_en;
      m_old_prior_en=rhs.m_old_prior_en;
      m_th_acc=rhs.m_th_acc; 
      m_th_rej=rhs.m_th_rej;
      m_ph_acc=rhs.m_ph_acc;
      m_ph_rej=rhs.m_ph_rej; 
      m_f_acc=rhs.m_f_acc;
      m_f_rej=rhs.m_f_rej;
      m_lam_acc=rhs.m_lam_acc; 
      m_lam_rej=rhs.m_lam_rej;
      m_lam_jump=rhs.m_lam_jump;
      m_Signal=rhs.m_Signal; 
      m_Signal_old=rhs.m_Signal_old; 
      m_ardfudge=rhs.m_ardfudge;
      return *this;
    }

    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;
    return ostr;
  }


  ///////////////////////////////////////////////////////////////////////////////////////////////////// 
  //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;
    float m_d_old;
    float m_d_prop;
    float m_d_prior; 
    float m_d_old_prior;
    float m_d_acc;
    float m_d_rej;
 
    float m_d_std; //second d param in multi-shell model - std in gamma dist of diffusivities - to get non-monoexponential decay. 
    float m_d_std_old;
    float m_d_std_prop;
    float m_d_std_prior; 
    float m_d_std_old_prior;
    float m_d_std_acc;
    float m_d_std_rej;
    
    float m_S0;
    float m_S0_old;
    float m_S0_prop;
    float m_S0_prior;
    float m_S0_old_prior;
    float m_S0_acc;
    float m_S0_rej;
    
    float m_tau;                  //Precision parameter (1/sigma^2) for Rician noise 
    float m_tau_old;
    float m_tau_prop;
    float m_tau_prior;
    float m_tau_old_prior;
    float m_tau_acc;
    float m_tau_rej;

    float m_f0;                  //Volume fraction for the unattenuated signal compartment 
    float m_f0_old;
    float m_f0_prop;
    float m_f0_prior;
    float m_f0_old_prior;
    float m_f0_acc;
    float m_f0_rej;

    float m_prior_en;             //Joint Prior
    float m_old_prior_en;
    float m_likelihood_en;        //Likelihood
    float m_old_likelihood_en;
    float m_energy;               //Posterior
    float m_old_energy;
    float m_ardfudge;
    ColumnVector m_iso_Signal;    //Vector that stores the signal from the isotropic compartment during the candidate/current state 
    ColumnVector m_iso_Signal_old; 

    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
    const bool m_rician;          //If true, use Rician noise model 
    const bool m_includef0;       //If true, include an unattenuated signal compartment in the model with fraction f0
    const bool m_ardf0;           //If true, use ard on the f0 compartment 

  public:
    //Constructor
    Multifibre(const ColumnVector& data,const ColumnVector& alpha, 
	       const ColumnVector& beta, const Matrix& b, int N ,float ardfudge=1, int modelnum=1, bool rician=false, bool inclf0=false, bool ardf0=false):
    m_ardfudge(ardfudge),m_data(data), m_alpha(alpha), m_beta(beta), m_bvals(b), m_modelnum(modelnum), m_rician(rician), m_includef0(inclf0), m_ardf0(ardf0){
      m_iso_Signal.ReSize(alpha.Nrows());
      m_iso_Signal=0;
      m_iso_Signal_old=m_iso_Signal;            //Initialize vectors that keep the signal from the isotropic compartment
     
      m_d_acc=0; m_d_rej=0;                
      m_d_std_acc=0; m_d_std_rej=0;
      m_S0_acc=0; m_S0_rej=0;
      m_tau_acc=0; m_tau_rej=0;
      m_f0_acc=0; m_f0_rej=0;
      m_d_prior=0; m_d_std_prior=0; m_S0_prior=0; m_f0_prior=0; m_tau_prior=0; 
      m_d=0; m_d_old=0; m_d_std=0; m_d_std_old=0; m_S0=0; m_S0_old=0; 
      m_f0=0.0; m_f0_old=0.0;   //Set this parameter to 0, it will be initialized later only if m_includef0 is true  
   }
    
    ~Multifibre(){}
    
    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 bool use_ard=true){
      Fibre fib(m_alpha,m_beta,m_bvals,m_d,m_ardfudge,th,ph,f,use_ard,m_modelnum,m_d_std);
       m_fibres.push_back(fib);
    }

    void addfibre(){
      Fibre fib(m_alpha,m_beta,m_bvals,m_d,m_ardfudge,m_modelnum,m_d_std);
      m_fibres.push_back(fib);
    }

    void initialise_energies(){
      compute_d_prior();
      if(m_modelnum==2)
	compute_d_std_prior();
      if (m_rician){
      	compute_tau_prior();
      }
      if (m_includef0)
	compute_f0_prior();
      compute_S0_prior();
      m_prior_en=0;
      compute_prior();
      m_likelihood_en=0;
      compute_iso_signal();                  
      compute_likelihood();
      compute_energy();
    }


    //Initialize standard deviations for the proposal distributions
    void initialise_props(){   
      m_S0_prop=m_S0/10.0; //must have set initial values before this is called;
      m_d_prop=m_d/10.0;
      m_d_std_prop=m_d_std/10.0;
      m_tau_prop=m_tau/2.0;
      m_f0_prop=0.2;
    }
    

    inline float get_d() const{ return m_d;}
    inline void set_d(const float d){ m_d=d; }

    inline float get_d_std() const{ return m_d_std;}
    inline void set_d_std(const float d_std){ m_d_std=d_std; }

    inline int get_nfibre(){return m_fibres.size();}
    
    inline float get_energy() const { return m_likelihood_en+m_prior_en;}
    inline float get_likelihood_energy() const { return m_likelihood_en;}
    inline float get_prior() const {return m_prior_en;}
    
    inline float get_S0() const{ return m_S0;}
    inline void set_S0(const float S0){ m_S0=S0; }
    
    inline float get_tau() const { return m_tau; }
    inline void set_tau(const float tau){ m_tau=tau;}
  
    inline float get_f0() const{ return m_f0;}
    inline void set_f0(const float f0){ m_f0=f0; }

    inline void report() const{
      OUT(m_d);
      OUT(m_d_old);
      OUT(m_d_prop);
      OUT(m_d_prior); 
      OUT(m_d_old_prior);
      OUT(m_d_acc);
      OUT(m_d_rej);
      OUT(m_d_std);
      OUT(m_d_std_old);
      OUT(m_d_std_prop);
      OUT(m_d_std_prior); 
      OUT(m_d_std_old_prior);
      OUT(m_d_std_acc);
      OUT(m_d_std_rej);
      OUT(m_S0);
      OUT(m_S0_old);
      OUT(m_S0_prop);
      OUT(m_S0_prior);
      OUT(m_S0_old_prior);
      OUT(m_S0_acc);
      OUT(m_S0_rej);
      OUT(m_prior_en);
      OUT(m_old_prior_en);
      OUT(m_likelihood_en);
      OUT(m_old_likelihood_en);
      OUT(m_energy);
      OUT(m_old_energy);
      for (unsigned int i=0;i<m_fibres.size();i++){cout <<"fibre "<<i<<endl;m_fibres[i].report();}
    }


    inline bool compute_d_prior(){
      m_d_old_prior=m_d_prior;
      if(m_d<0 || m_d>0.008)
	return true;
      else{
	m_d_prior=0;
	return false;
      }
    }


    //More restrictive prior for the model with f0
    //particularly useful for the CSF voxels 
    inline bool compute_d_prior_f0(){
      m_d_old_prior=m_d_prior;
      if(m_d<0 || m_d>0.008)
	return true;
      else{
	m_d_prior=0;
	return false;
      }
    }


    inline bool compute_d_std_prior(){
      m_d_std_old_prior=m_d_std_prior;
      if(m_d_std<=0 || m_d_std>0.01)
	return true;
      else{
	//m_d_std_prior=0;
	m_d_std_prior=std::log(m_d_std);
	return false;
      }
    }
    
    inline bool compute_S0_prior(){
      m_S0_old_prior=m_S0_prior;
      if(m_S0<0) return true;
      else
	{    
	  m_S0_prior=0;
	  return false;
	}
    }


    inline bool compute_tau_prior(){
      m_tau_old_prior=m_tau_prior;
      if(m_tau<=0)
	return true;
      else{
	m_tau_prior=0;
	return false;
      }
    }


    inline bool compute_f0_prior(){
      m_f0_old_prior=m_f0_prior;
      if (m_f0<=0 || m_f0>=1)
	return true;
      else{
	if(!m_ardf0)     //Without ARD
	  m_f0_prior=0;
	else              //With ARD
	  m_f0_prior=std::log(m_f0);
	return false;	
      }
    } 


    //Check if sum of volume fractions is >1
    bool reject_f_sum(){
      float fsum=m_f0;
      for(unsigned int f=0;f<m_fibres.size();f++){
	fsum+=m_fibres[f].get_f();	
      }
      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;
      if(m_modelnum==2)
	m_prior_en=m_prior_en+m_d_std_prior;
      if(m_rician)
	m_prior_en=m_prior_en+m_tau_prior;
      if (m_includef0)
	m_prior_en=m_prior_en+m_f0_prior;
      for(unsigned int f=0;f<m_fibres.size(); f++){
	m_prior_en=m_prior_en+m_fibres[f].get_prior();
      } 
    }
    

    void compute_iso_signal(){               //Function that computes the signal from the isotropic compartment 
      m_iso_Signal_old=m_iso_Signal;
      if(m_modelnum==1 || m_d_std<1e-5){
	for(int i=1;i<=m_alpha.Nrows();i++)
	   m_iso_Signal(i)=exp(-m_d*m_bvals(1,i));
      }
      else if(m_modelnum==2){
	for(int i=1;i<=m_alpha.Nrows();i++){
	  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()),pred2;
      pred=0; pred2=pred;
      float fsum=m_f0;
      for(unsigned int f=0;f<m_fibres.size();f++){   //Signal from the anisotropic compartments
	pred=pred+m_fibres[f].get_f()*m_fibres[f].getSignal();
	fsum+=m_fibres[f].get_f();                   //Total anisotropic volume fraction
      }

      for(int i=1;i<=pred.Nrows();i++){
      	pred(i)=m_S0*(pred(i)+(1-fsum)*m_iso_Signal(i)+m_f0);   //Add the signal from the isotropic compartment     
      }                                                         //and multiply by S0 to get the total signal
      
      if (!m_rician){     //Likelihood energy for Gaussian noise
	float sumsquares=(m_data-pred).SumSquare();             //Sum of squared residuals 
	m_likelihood_en=(m_data.Nrows()/2.0)*log(sumsquares/2.0);
      }
      else{   //Likelihood energy for Rician noise
	for(int i=1;i<=pred.Nrows();i++)
	  pred2(i)=std::log(m_data(i))-0.5*m_tau*(m_data(i)*m_data(i)+pred(i)*pred(i))+logIo(m_tau*pred(i)*m_data(i));     
	m_likelihood_en=-m_data.Nrows()*std::log(m_tau)-pred2.Sum();
      }
   }
    
    
    inline void compute_energy(){
      m_old_energy=m_energy;
      m_energy=m_prior_en+m_likelihood_en;
      //cout<<m_prior_en<<" "<<m_likelihood_en<<endl;
    }
   
    /*
    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_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);
      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;
      bool rejflag;
      m_d_old=m_d;
      m_d+=normrnd().AsScalar()*m_d_prop;
      if (m_includef0)
      	rejflag=compute_d_prior_f0();
      else
	rejflag=compute_d_prior();
      for(unsigned int f=0;f<m_fibres.size();f++)
	m_fibres[f].compute_signal();
      compute_iso_signal();        
      return rejflag;
    };
    

    inline void accept_d(){
      m_d_acc++;      
    }

    
    inline void reject_d(){
      m_d=m_d_old;
      m_d_prior=m_d_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;   
      m_d_rej++;
    }


    inline bool propose_d_std(){
      m_d_std_old=m_d_std;
      m_d_std+=normrnd().AsScalar()*m_d_std_prop;
      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();   
      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;   
      m_d_std_rej++;
    }
    

    inline bool propose_S0(){
      m_S0_old=m_S0;
      m_S0+=normrnd().AsScalar()*m_S0_prop;
      bool rejflag=compute_S0_prior();//inside this it stores the old prior
      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;
      m_prior_en=m_old_prior_en;
      m_S0_rej++;
    }
    

    inline bool propose_tau(){
      m_tau_old=m_tau;
      m_tau+=normrnd().AsScalar()*m_tau_prop;
      bool rejflag=compute_tau_prior();//inside this it stores the old prior
      return rejflag;
    };
    

    inline void accept_tau(){
      m_tau_acc++;      
    }
    
    inline void reject_tau(){
      m_tau=m_tau_old;
      m_tau_prior=m_tau_old_prior;
      m_prior_en=m_old_prior_en;
      m_tau_rej++;
    }
    

    inline bool propose_f0(){
      m_f0_old=m_f0;
      m_f0+=normrnd().AsScalar()*m_f0_prop;
      bool rejflag=compute_f0_prior();
      compute_prior();
      return rejflag;
    };
    

    inline void accept_f0(){
      m_f0_acc++;
    }

    inline void reject_f0(){
      m_f0=m_f0_old;
      m_f0_prior=m_f0_old_prior;
      m_prior_en=m_old_prior_en;
      m_f0_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);
      if(m_modelnum==2){
	m_d_std_prop*=sqrt(float(m_d_std_acc+1)/float(m_d_std_rej+1));
	m_d_std_prop=min(m_d_std_prop,maxfloat);
	m_d_std_acc=0; 
	m_d_std_rej=0;
      }
      if (m_rician){
      	m_tau_prop*=sqrt(float(m_tau_acc+1)/float(m_tau_rej+1));
	m_tau_prop=min(m_tau_prop,maxfloat);
	m_tau_acc=0; 
	m_tau_rej=0;
      }  
      if (m_includef0){
      	m_f0_prop*=sqrt(float(m_f0_acc+1)/float(m_f0_rej+1));
	m_f0_prop=min(m_f0_prop,maxfloat);
	m_f0_acc=0; 
	m_f0_rej=0;
      } 
      m_S0_prop*=sqrt(float(m_S0_acc+1)/float(m_S0_rej+1));
      m_S0_prop=min(m_S0_prop,maxfloat);
      m_d_acc=0; 
      m_d_rej=0;
      m_S0_acc=0; 
      m_S0_rej=0;
      for(unsigned int f=0; f<m_fibres.size();f++){
	m_fibres[f].update_proposals();
      }
      
    }
    
    
    //Function that performs a single MCMC iteration
    void jump( bool can_use_ard=true ){
      if (m_includef0){     //Try f0 
	if(!propose_f0()){  
	  if(!reject_f_sum()){
	    compute_prior();
	    compute_likelihood();
	    compute_energy();
	    if(test_energy())
	      accept_f0();
	    else{
	      reject_f0();
	      restore_energy();
	    }
	  }
	  else    //else for rejecting fsum>1
	     reject_f0();
	}
	else     //else for rejecting rejflag returned from propose_f()
	  reject_f0();
      }


      if(m_rician){     //Try tau
	if(!propose_tau()){
  	  compute_prior();
	  compute_likelihood();
	  compute_energy();
	  if(test_energy())
	    accept_tau();
	  else{
	    reject_tau();
	    restore_energy();
	  }
	}
	else 
	  reject_tau();
	  } 
   
      if(!propose_d()){          //Try d 
	compute_prior();
	compute_likelihood();
	compute_energy();
	if(test_energy()){
	  accept_d();
	}
	else{
	  reject_d();
	  restore_energy();
	}
      }
      else 
	reject_d();
      
      
      if(m_modelnum==2){     //Try d_std
	if(!propose_d_std()){
	  compute_prior();
	  compute_likelihood();
	  compute_energy();
	  if(test_energy()){
	    accept_d_std();
	  }
	  else{
	    reject_d_std();
	    restore_energy();
	  }
	}
	else 
	  reject_d_std();
      }


      if(!propose_S0()){    //Try S0
	compute_prior();
	compute_likelihood();
	compute_energy();
	if(test_energy())
	  accept_S0();
	else{
	  reject_S0();
	  restore_energy();
	}
      }
      else
	reject_S0();
      
      



      for(unsigned int f=0;f<m_fibres.size();f++){  //For each fibre
	if(!m_fibres[f].propose_th()){   //Try theta
	  compute_prior();
	  compute_likelihood();
	  compute_energy();

	  if(test_energy())
	    m_fibres[f].accept_th();
	  else{
	    m_fibres[f].reject_th();
	    restore_energy();
	  }
	}
	else 
	  m_fibres[f].reject_th();
	
	
	if(!m_fibres[f].propose_ph()){  //Try phi
	    compute_prior();
	    compute_likelihood();
	    compute_energy();
	    if(test_energy())
	      m_fibres[f].accept_ph();
	    else{
	      m_fibres[f].reject_ph();
	      restore_energy();
	    }
	  }
	else
	  m_fibres[f].reject_ph();
	 
	  
	if(!m_fibres[f].propose_f( can_use_ard )){  //Try f 
	    if(!reject_f_sum()){
	      compute_prior();
	      compute_likelihood();
	      compute_energy();
	      if(test_energy())
		m_fibres[f].accept_f();
	      else{
		m_fibres[f].reject_f();
		restore_energy();
	      }
	    }
	    else   //else for rejectin fsum>1
	      m_fibres[f].reject_f();
	}
	else    //else for rejecting rejflag returned from propose_f()
	  m_fibres[f].reject_f();
	  
	  
//	   if(!m_fibres[f].propose_lam()){
// 	    compute_prior();
// 	    compute_energy();
// 	    if(test_energy()){
// 	      m_fibres[f].accept_lam();
// 	    }
// 	    else{
// 	      m_fibres[f].reject_lam();
// 	      restore_energy_no_lik();
// 	    }
// 	  }
// 	  else{
// 	    m_fibres[f].reject_lam();
// 	  }
      }
    }

    
    Multifibre& operator=(const Multifibre& rhs){
      m_fibres=rhs.m_fibres;
      m_d=rhs.m_d;
      m_d_old=rhs.m_d_old;
      m_d_prop=rhs.m_d_prop;
      m_d_prior=rhs.m_d_prior; 
      m_d_old_prior=rhs.m_d_old_prior;
      m_d_acc=rhs.m_d_acc;
      m_d_rej=rhs.m_d_rej;
      m_d_std=rhs.m_d_std;
      m_d_std_old=rhs.m_d_std_old;
      m_d_std_prop=rhs.m_d_std_prop;
      m_d_std_prior=rhs.m_d_std_prior; 
      m_d_std_old_prior=rhs.m_d_std_old_prior;
      m_d_std_acc=rhs.m_d_std_acc;
      m_d_std_rej=rhs.m_d_std_rej;
      m_S0=rhs.m_S0;
      m_S0_old=rhs.m_S0_old;
      m_S0_prop=rhs.m_S0_prop;
      m_S0_prior=rhs.m_S0_prior;
      m_S0_old_prior=rhs.m_S0_old_prior;
      m_S0_acc=rhs.m_S0_acc;
      m_S0_rej=rhs.m_S0_rej;
      m_f0=rhs.m_f0;
      m_f0_old=rhs.m_f0_old;
      m_f0_prop=rhs.m_f0_prop;
      m_f0_prior=rhs.m_f0_prior;
      m_f0_old_prior=rhs.m_f0_old_prior;
      m_f0_acc=rhs.m_f0_acc;
      m_f0_rej=rhs.m_f0_rej;
      m_tau=rhs.m_tau;
      m_tau_old=rhs.m_tau_old;
      m_tau_prop=rhs.m_tau_prop;
      m_tau_prior=rhs.m_tau_prior;
      m_tau_old_prior=rhs.m_tau_old_prior;
      m_tau_acc=rhs.m_tau_acc;
      m_tau_rej=rhs.m_tau_rej;
      m_prior_en=rhs.m_prior_en;
      m_old_prior_en=rhs.m_old_prior_en;
      m_likelihood_en=rhs.m_likelihood_en;
      m_old_likelihood_en=rhs.m_old_likelihood_en;
      m_energy=rhs.m_energy;
      m_old_energy=rhs.m_old_energy;
      m_ardfudge=rhs.m_ardfudge;
      m_iso_Signal=rhs.m_iso_Signal;              
      m_iso_Signal_old=rhs.m_iso_Signal_old;
      return *this;
      }
 
   
  };
  
}

#endif