From a33476e37f85ef52d5f6ccd7e6694a6e4c3b7dfd Mon Sep 17 00:00:00 2001
From: Stamatios Sotiropoulos <stam@fmrib.ox.ac.uk>
Date: Mon, 10 Oct 2011 11:29:35 +0000
Subject: [PATCH] Include noise floor for model2

---
 diffmodels.cc | 74 +++++++++++++++++++++++++++++++++++++++------------
 diffmodels.h  | 12 +++++++--
 xfibres.cc    | 45 ++++++++++++++++++-------------
 3 files changed, 94 insertions(+), 37 deletions(-)

diff --git a/diffmodels.cc b/diffmodels.cc
index cb5c250..09a35a1 100644
--- a/diffmodels.cc
+++ b/diffmodels.cc
@@ -1156,7 +1156,7 @@ boost::shared_ptr<BFMatrix> PVM_single::hess(const NEWMAT::ColumnVector& p,boost
 void PVM_multi::fit(){
 
   // initialise with simple pvm
-  PVM_single pvm1(Y,bvecs,bvals,nfib);
+  PVM_single pvm1(Y,bvecs,bvals,nfib,m_include_f0);
   pvm1.fit();
 
   float _a,_b;
@@ -1168,10 +1168,13 @@ void PVM_multi::fit(){
   start(2) = _a;
   start(3) = _b;
   for(int i=1,ii=4;i<=nfib;i++,ii+=3){
-    start(ii) = pvm1.get_f(i);
+    start(ii) = f2x(pvm1.get_f(i));   //Is this a bug?
     start(ii+1) = pvm1.get_th(i);
     start(ii+2) = pvm1.get_ph(i);
   }
+  if (m_include_f0)
+    start(nparams)=f2x(pvm1.get_f0());
+
   
   // do the fit
   NonlinParam  lmpar(start.Nrows(),NL_LM); 
@@ -1192,10 +1195,12 @@ void PVM_multi::fit(){
     m_th(k) = final_par(i+1);
     m_ph(k) = final_par(i+2);
   }
+  if (m_include_f0)
+    m_f0=x2f(final_par(nparams));
   sort();
   fix_fsum();
-
 }
+
 void PVM_multi::sort(){
   vector< pair<float,int> > fvals(nfib);
   ColumnVector ftmp(nfib),thtmp(nfib),phtmp(nfib);
@@ -1211,13 +1216,19 @@ void PVM_multi::sort(){
     m_ph(i) = phtmp(fvals[ii].second);
   }
 }
+
+
 void PVM_multi::fix_fsum(){
   float sumf=0;
+  if (m_include_f0) 
+    sumf=m_f0;
   for(int i=1;i<=nfib;i++){
     sumf+=m_f(i);
     if(sumf>=1){for(int j=i;j<=nfib;j++)m_f(j)=0;break;}
   }
 }
+
+
 ReturnMatrix PVM_multi::get_prediction()const{
   ColumnVector pred(npts);
   ColumnVector p(nparams);
@@ -1230,10 +1241,14 @@ ReturnMatrix PVM_multi::get_prediction()const{
     p(kk+1) = m_th(k);
     p(kk+2) = m_ph(k);
   }
+  if (m_include_f0)
+    p(nparams)=f2x(m_f0);
   pred = forwardModel(p);
   pred.Release();
   return pred;
 }
+
+
 NEWMAT::ReturnMatrix PVM_multi::forwardModel(const NEWMAT::ColumnVector& p)const{
   ColumnVector pred(npts);
   pred = 0;
@@ -1258,11 +1273,17 @@ NEWMAT::ReturnMatrix PVM_multi::forwardModel(const NEWMAT::ColumnVector& p)const
     for(int k=1;k<=nfib;k++){
       val += fs(k)*anisoterm(i,_a,_b,x.Row(k).t());
     }
-    pred(i) = p(1)*((1-sumf)*isoterm(i,_a,_b)+val); 
+    if (m_include_f0){
+      float temp_f0=x2f(p(nparams));
+      pred(i) = std::abs(p(1))*(temp_f0+(1-sumf-temp_f0)*isoterm(i,_a,_b)+val);
+    } 
+    else
+      pred(i) = std::abs(p(1))*((1-sumf)*isoterm(i,_a,_b)+val); 
   }  
   pred.Release();
   return pred;
 }
+
 double PVM_multi::cf(const NEWMAT::ColumnVector& p)const{
   //cout<<"CF"<<endl;
   //OUT(p.t());
@@ -1287,8 +1308,13 @@ double PVM_multi::cf(const NEWMAT::ColumnVector& p)const{
     err = 0.0;
     for(int k=1;k<=nfib;k++){
       err += fs(k)*anisoterm(i,_a,_b,x.Row(k).t());
+    } 
+    if (m_include_f0){
+      float temp_f0=x2f(p(nparams));
+      err = (std::abs(p(1))*(temp_f0+(1-sumf-temp_f0)*isoterm(i,_a,_b)+err) - Y(i)); 
     }
-    err = (std::abs(p(1))*((1-sumf)*isoterm(i,_a,_b)+err) - Y(i)); 
+    else
+      err = (std::abs(p(1))*((1-sumf)*isoterm(i,_a,_b)+err) - Y(i)); 
     cfv += err*err; 
   }  
   //OUT(cfv);
@@ -1338,12 +1364,21 @@ NEWMAT::ReturnMatrix PVM_multi::grad(const NEWMAT::ColumnVector& p)const{
       // ph
       J(i,kk+2) = std::abs(p(1))*fs(k)*anisoterm_ph(i,_a,_b,xx,p(kk+1),p(kk+2));
     }
-    sig = std::abs(p(1))*((1-sumf)*isoterm(i,_a,_b)+sig);
+    if (m_include_f0){
+      float temp_f0=x2f(p(nparams));
+      //derivative with respect to f0
+      J(i,nparams)= std::abs(p(1))*(1-isoterm(i,_a,_b))*two_pi*sign(p(nparams))*1/(1+p(nparams)*p(nparams));
+      sig=std::abs(p(1))*(temp_f0+(1-sumf-temp_f0)*isoterm(i,_a,_b)+sig);
+      J(i,2) += (p(2)>0?1.0:-1.0)*std::abs(p(1))*(1-sumf-temp_f0)*isoterm_a(i,_a,_b);
+      J(i,3) += (p(3)>0?1.0:-1.0)*std::abs(p(1))*(1-sumf-temp_f0)*isoterm_b(i,_a,_b);
+    }
+    else{
+      sig = std::abs(p(1))*((1-sumf)*isoterm(i,_a,_b)+sig);
+      J(i,2) += (p(2)>0?1.0:-1.0)*std::abs(p(1))*(1-sumf)*isoterm_a(i,_a,_b);
+      J(i,3) += (p(3)>0?1.0:-1.0)*std::abs(p(1))*(1-sumf)*isoterm_b(i,_a,_b);
+    }
     diff(i) = sig - Y(i);
-    // other stuff for derivatives
     J(i,1) = (p(1)>0?1.0:-1.0)*sig/p(1);
-    J(i,2) += (p(2)>0?1.0:-1.0)*std::abs(p(1))*(1-sumf)*isoterm_a(i,_a,_b);
-    J(i,3) += (p(3)>0?1.0:-1.0)*std::abs(p(1))*(1-sumf)*isoterm_b(i,_a,_b);
   }
   
   gradv = 2*J.t()*diff;
@@ -1378,7 +1413,6 @@ boost::shared_ptr<BFMatrix> PVM_multi::hess(const NEWMAT::ColumnVector& p,boost:
   }
   ////////////////////////////////////
   Matrix J(npts,nparams);
-  ColumnVector diff(npts);
   float sig;
   for(int i=1;i<=Y.Nrows();i++){
     sig = 0;
@@ -1401,16 +1435,22 @@ boost::shared_ptr<BFMatrix> PVM_multi::hess(const NEWMAT::ColumnVector& p,boost:
       // ph
       J(i,kk+2) = std::abs(p(1))*fs(k)*anisoterm_ph(i,_a,_b,xx,p(kk+1),p(kk+2));
     }
-    sig = std::abs(p(1))*((1-sumf)*isoterm(i,_a,_b)+sig);
-    diff(i) = sig - Y(i);
-    // other stuff for derivatives
+    if (m_include_f0){
+      float temp_f0=x2f(p(nparams));
+      //derivative with respect to f0
+      J(i,nparams)= std::abs(p(1))*(1-isoterm(i,_a,_b))*two_pi*sign(p(nparams))*1/(1+p(nparams)*p(nparams));
+      sig=std::abs(p(1))*(temp_f0+(1-sumf-temp_f0)*isoterm(i,_a,_b)+sig);
+      J(i,2) += (p(2)>0?1.0:-1.0)*std::abs(p(1))*(1-sumf-temp_f0)*isoterm_a(i,_a,_b);
+      J(i,3) += (p(3)>0?1.0:-1.0)*std::abs(p(1))*(1-sumf-temp_f0)*isoterm_b(i,_a,_b);
+    }
+    else{
+      sig = std::abs(p(1))*((1-sumf)*isoterm(i,_a,_b)+sig);
+      J(i,2) += (p(2)>0?1.0:-1.0)*std::abs(p(1))*(1-sumf)*isoterm_a(i,_a,_b);
+      J(i,3) += (p(3)>0?1.0:-1.0)*std::abs(p(1))*(1-sumf)*isoterm_b(i,_a,_b);
+    }
     J(i,1) = (p(1)>0?1.0:-1.0)*sig/p(1);
-    J(i,2) += (p(2)>0?1.0:-1.0)*std::abs(p(1))*(1-sumf)*isoterm_a(i,_a,_b);
-    J(i,3) += (p(3)>0?1.0:-1.0)*std::abs(p(1))*(1-sumf)*isoterm_b(i,_a,_b);
-
   }
   
-
   for (int i=1; i<=p.Nrows(); i++){
     for (int j=i; j<=p.Nrows(); j++){
       sig = 0.0;
diff --git a/diffmodels.h b/diffmodels.h
index fab457f..0b79afa 100644
--- a/diffmodels.h
+++ b/diffmodels.h
@@ -461,9 +461,12 @@ class PVM_multi : public PVM, public NonlinCF {
 public:
   PVM_multi(const ColumnVector& iY,
 	    const Matrix& ibvecs, const Matrix& ibvals,
-	    const int& nfibres):PVM(iY,ibvecs,ibvals,nfibres){
+	    const int& nfibres, bool incl_f0=false):PVM(iY,ibvecs,ibvals,nfibres), m_include_f0(incl_f0){
 
-    nparams = nfib*3 + 3;
+   if (m_include_f0)
+      nparams = nfib*3 + 4; 
+    else
+      nparams = nfib*3 + 3;
 
     m_f.ReSize(nfib);
     m_th.ReSize(nfib);
@@ -506,10 +509,13 @@ public:
       cout << "TH" << ii << "   :" << p(i+1) << endl; 
       cout << "PH" << ii << "   :" << p(i+2) << endl; 
     }
+    if (m_include_f0)
+      cout << "f0    :" << x2f(p(nparams)) << endl;
   }
 
   float get_s0()const{return m_s0;}
   float get_d()const{return m_d;}
+  float get_f0()const{return m_f0;}
   float get_d_std()const{return m_d_std;}
   ColumnVector get_f()const{return m_f;}
   ColumnVector get_th()const{return m_th;}
@@ -536,10 +542,12 @@ private:
   int   nparams;
   float m_s0;
   float m_d;
+  float m_f0;
   float m_d_std;
   ColumnVector m_f;
   ColumnVector m_th;
   ColumnVector m_ph;
+  const bool m_include_f0;   //Indicate whether f0 will be used in the model (an unattenuated signal compartment)
 };
 
 
diff --git a/xfibres.cc b/xfibres.cc
index ab9fe40..93fb4b9 100644
--- a/xfibres.cc
+++ b/xfibres.cc
@@ -643,27 +643,36 @@ public:
     else{ 
       //////////////////////////////////////////////////////
       // model 2 : non-mono-exponential
-      if (opts.f0.value())
-	m_multifibre.set_f0(0.001); //Need to include f0 in the non-linear initialization of model2 as well!! For now set it initially to almost zero
-
-      PVM_multi pvm(m_data,m_bvecs,m_bvals,opts.nfibres.value());
+      float pvmS0, pvmd, pvmd_std, pvmf0=0.001;
+      ColumnVector pvmf,pvmth,pvmph;
+      
+      PVM_multi pvm(m_data,m_bvecs,m_bvals,opts.nfibres.value(),opts.f0.value());
       pvm.fit();
 
-      m_multifibre.set_S0(pvm.get_s0());
-      if(pvm.get_d()>=0)
-	m_multifibre.set_d(pvm.get_d());
-      else
-	m_multifibre.set_d(2e-3);
-      if(pvm.get_d_std()>=0)
-	m_multifibre.set_d_std(pvm.get_d());
-      else
-	m_multifibre.set_d(2e-3);
+      pvmf  = pvm.get_f();  pvmth = pvm.get_th(); pvmph = pvm.get_ph(); pvmd_std=pvm.get_d_std();
+      pvmS0 = pvm.get_s0(); pvmd  = pvm.get_d();  predicted_signal=pvm.get_prediction();
+      
+      if (opts.f0.value()){
+	  pvmf0=pvm.get_f0();
+	  //If the full model gives values that are implausible, or we are in a CSF voxel (f1<0.05)
+	  //then fit a model without the f0 and drive f0_init to almost zero 
+	  if ((opts.nfibres.value()>0 && pvmf(1)<0.05) || pvmd>0.007 || pvmf0>0.4){
+	    PVM_multi pvm2(m_data,m_bvecs,m_bvals,opts.nfibres.value(),false);
+	    pvm2.fit();
+	    pvmf0=0.001; pvmS0=pvm2.get_s0(); pvmd=pvm2.get_d(); pvmd_std=pvm2.get_d_std();
+	    pvmf  = pvm2.get_f();  pvmth = pvm2.get_th(); pvmph = pvm2.get_ph();
+	    predicted_signal=pvm2.get_prediction();
+	  }
+	  m_multifibre.set_f0(pvmf0);
+      }
 
-      ColumnVector pvmf,pvmth,pvmph;
-      pvmf  = pvm.get_f();
-      pvmth = pvm.get_th();
-      pvmph = pvm.get_ph();
+      if(pvmd<0) pvmd=2e-3;
+      if(pvmd_std<0) pvmd_std=2e-3;
 
+      m_multifibre.set_S0(pvmS0);
+      m_multifibre.set_d(pvmd);
+      m_multifibre.set_d_std(pvmd_std);
+     
       if(opts.nfibres.value()>0){
 	m_multifibre.addfibre(pvmth(1),
 			      pvmph(1),
@@ -677,7 +686,7 @@ public:
 	}
 	
       }
-      residuals=m_data-pvm.get_prediction();
+      residuals=m_data-predicted_signal;
     }
     residuals.Release();
     return residuals;   
-- 
GitLab