ANIMA  4.0
animaB1GammaMixtureT2RelaxometryCostFunction.cxx
Go to the documentation of this file.
2 
4 
7 
8 namespace anima
9 {
10 
13 {
14  m_TestedParameters.SetSize(this->GetNumberOfParameters());
15 
16  for (unsigned int i = 0;i < this->GetNumberOfParameters();++i)
17  m_TestedParameters[i] = parameters[i];
18 
19  this->PrepareDataForLLS();
20 
22 
23  return m_SigmaSquare;
24 }
25 
26 void
28 {
29  unsigned int numT2Signals = m_T2RelaxometrySignals.size();
30  unsigned int numDistributions = m_GammaMeans.size();
31 
32  unsigned int numParameters = this->GetNumberOfParameters();
33 
34  if (!m_ConstrainedParameters)
35  {
36  for (unsigned int i = 1;i < numParameters;++i)
37  m_GammaMeans[i] = m_TestedParameters[i];
38  }
39  else
40  m_GammaMeans[1] = m_TestedParameters[1];
41 
42  double b1Value = m_TestedParameters[0];
43 
44  m_T2SignalSimulator.SetNumberOfEchoes(numT2Signals);
45  m_T2SignalSimulator.SetEchoSpacing(m_EchoSpacing);
46  m_T2SignalSimulator.SetExcitationFlipAngle(m_ExcitationFlipAngle);
47 
48  // Contains int_{space of ith distribution} EPG(t2, b1, jth echo) Gamma(t2, mu_i, sigma_i) d t2
49  m_PredictedSignalAttenuations.set_size(numT2Signals,numDistributions);
50 
52  B1GammaDistributionIntegrand t2Integrand(m_T2SignalSimulator,epgVectors);
53  t2Integrand.SetT1Value(m_T1Value);
54  t2Integrand.SetFlipAngle(b1Value);
55 
57 
58  for (unsigned int i = 0;i < numDistributions;++i)
59  {
60  t2Integrand.SetGammaMean(m_GammaMeans[i]);
61  t2Integrand.SetGammaVariance(m_GammaVariances[i]);
62  epgVectors.clear();
63 
64  double minInterestZoneValue = std::max(0.0, m_GammaMeans[i] - 5.0 * std::sqrt(m_GammaVariances[i]));
65  double theoreticalMinValue = m_GammaMeans[i] - i * std::sqrt(m_GammaVariances[i]);
66  double maxInterestZoneValue = m_GammaMeans[i] + 5.0 * std::sqrt(m_GammaVariances[i]);
67  if (theoreticalMinValue < 0.0)
68  maxInterestZoneValue -= theoreticalMinValue;
69 
70  glQuad.SetInterestZone(minInterestZoneValue, maxInterestZoneValue);
71 
72  for (unsigned int j = 0;j < numT2Signals;++j)
73  {
74  t2Integrand.SetEchoNumber(j);
75  m_PredictedSignalAttenuations(j,i) = glQuad.GetIntegralValue(t2Integrand);
76  }
77  }
78 
79  m_CholeskyMatrix.set_size(numDistributions,numDistributions);
80  m_FSignals.SetSize(numDistributions);
81 
82  for (unsigned int i = 0;i < numDistributions;++i)
83  {
84  m_FSignals[i] = 0.0;
85  for (unsigned int j = 0;j < numT2Signals;++j)
86  m_FSignals[i] += m_PredictedSignalAttenuations(j,i) * m_T2RelaxometrySignals[j];
87 
88  for (unsigned int j = i;j < numDistributions;++j)
89  {
90  m_CholeskyMatrix(i,j) = 0;
91  for (unsigned int k = 0;k < numT2Signals;++k)
92  m_CholeskyMatrix(i,j) += m_PredictedSignalAttenuations(k,i) * m_PredictedSignalAttenuations(k,j);
93 
94  if (i != j)
95  m_CholeskyMatrix(j,i) = m_CholeskyMatrix(i,j);
96  }
97  }
98 
99  m_CholeskySolver.SetInputMatrix(m_CholeskyMatrix);
100  m_CholeskySolver.PerformDecomposition();
101  m_OptimalT2Weights = m_CholeskySolver.SolveLinearSystem(m_FSignals);
102 
103  bool nnlsNeeded = false;
104 
105  for (unsigned int i = 0;i < numDistributions;++i)
106  {
107  if (m_OptimalT2Weights[i] <= 0.0)
108  {
109  nnlsNeeded = true;
110  break;
111  }
112  }
113 
114  if (nnlsNeeded)
115  {
116  m_NNLSBordersOptimizer->SetDataMatrix(m_CholeskyMatrix);
117  m_NNLSBordersOptimizer->SetPoints(m_FSignals);
118  m_NNLSBordersOptimizer->SetSquaredProblem(true);
119  m_NNLSBordersOptimizer->StartOptimization();
120 
121  m_OptimalT2Weights = m_NNLSBordersOptimizer->GetCurrentPosition();
122  }
123 
124  m_CompartmentSwitches.resize(numDistributions);
125  for (unsigned int i = 0;i < numDistributions;++i)
126  {
127  if (m_OptimalT2Weights[i] > 0.0)
128  m_CompartmentSwitches[i] = true;
129  else
130  {
131  m_CompartmentSwitches[i] = false;
132  m_OptimalT2Weights[i] = 0.0;
133  }
134  }
135 
136  m_Residuals.set_size(numT2Signals);
137 }
138 
139 void
141 {
142  unsigned int numT2Signals = m_T2RelaxometrySignals.size();
143  unsigned int numDistributions = m_GammaMeans.size();
144 
145  m_SigmaSquare = 0.0;
146 
147  for (unsigned int i = 0;i < numT2Signals;++i)
148  {
149  m_Residuals[i] = m_T2RelaxometrySignals[i];
150  for (unsigned int j = 0;j < numDistributions;++j)
151  m_Residuals[i] -= m_PredictedSignalAttenuations(i,j) * m_OptimalT2Weights[j];
152 
153  m_SigmaSquare += m_Residuals[i] * m_Residuals[i];
154  }
155 
156  m_SigmaSquare /= numT2Signals;
157 }
158 
159 /* main part where we calculate the derivative updates */
161 {
162  unsigned int nbValues = m_T2RelaxometrySignals.size();
163  unsigned int numDistributions = m_GammaMeans.size();
164 
165  this->PrepareDataForDerivative();
166  unsigned int numOnDistributions = m_GramMatrix.rows();
167 
168  // Assume get derivative is called with the same parameters as GetValue just before
169  for (unsigned int i = 0;i < m_TestedParameters.size();++i)
170  {
171  if (m_TestedParameters[i] != parameters[i])
172  {
173  std::cerr << parameters << std::endl;
174  itkExceptionMacro("Get derivative not called with the same parameters as GetValue, suggestive of NaN...");
175  }
176  }
177 
178  unsigned int numParameters = this->GetNumberOfParameters();
179  derivative.set_size(numParameters);
180 
181  MatrixType derivativeMatrix(nbValues, numParameters);
182  derivativeMatrix.fill(0.0);
183 
184  std::vector <double> DFw(nbValues,0.0);
185  std::vector <double> tmpVec(numOnDistributions,0.0);
186 
187  for (unsigned int k = 0;k < numParameters;++k)
188  {
189  // First, compute DFw = DF w
190  std::fill(DFw.begin(),DFw.end(),0.0);
191  for (unsigned int i = 0;i < nbValues;++i)
192  {
193  DFw[i] = 0.0;
194  for (unsigned int j = 0;j < numDistributions;++j)
195  DFw[i] += m_SignalAttenuationsJacobian[k](i,j) * m_OptimalT2Weights[j];
196  }
197 
198  // Then, compute tmpVec = F^T DF w - (DF)^T Residuals
199  unsigned int pos = 0;
200  for (unsigned int j = 0;j < numDistributions;++j)
201  {
202  if (!m_CompartmentSwitches[j])
203  continue;
204 
205  tmpVec[pos] = 0.0;
206 
207  for (unsigned int i = 0;i < nbValues;++i)
208  tmpVec[pos] += m_PredictedSignalAttenuations(i,j) * DFw[i] - m_SignalAttenuationsJacobian[k](i,j) * m_Residuals[i];
209 
210  ++pos;
211  }
212 
213  // Finally, get derivative = FMatrixInverseG tmpVec - DFw
214  for (unsigned int i = 0;i < nbValues;++i)
215  {
216  double derivativeVal = - DFw[i];
217 
218  for (unsigned int j = 0;j < numOnDistributions;++j)
219  derivativeVal += m_FMatrixInverseG(i,j) * tmpVec[j];
220 
221  derivativeMatrix(i,k) = derivativeVal;
222  }
223  }
224 
225  bool problem = false;
226  for (unsigned int i = 0;i < numParameters;++i)
227  {
228  if (!std::isfinite(derivativeMatrix(0,i)))
229  {
230  problem = true;
231  break;
232  }
233  }
234 
235  if (problem)
236  {
237  std::cerr << "Derivative matrix: " << derivativeMatrix << std::endl;
238  std::cerr << "Optimal weights: " << m_OptimalT2Weights << std::endl;
239  std::cerr << "Gram matrix: " << m_GramMatrix << std::endl;
240  std::cerr << "Residuals: " << m_Residuals << std::endl;
241 
242  std::cerr << "Params: " << parameters << std::endl;
243  itkExceptionMacro("Non finite derivative");
244  }
245 
246  derivative.set_size(numParameters);
247 
248  for (unsigned int i = 0;i < numParameters;++i)
249  {
250  derivative[i] = 0.0;
251  for (unsigned int j = 0;j < nbValues;++j)
252  derivative[i] += m_Residuals[j] * derivativeMatrix(j,i);
253 
254  derivative[i] *= 2.0 / m_SigmaSquare;
255  }
256 }
257 
258 void
260 {
261  unsigned int numT2Signals = m_T2RelaxometrySignals.size();
262  unsigned int numDistributions = m_GammaMeans.size();
263 
264  unsigned int numOnDistributions = 0;
265  for (unsigned int i = 0;i < numDistributions;++i)
266  numOnDistributions += m_CompartmentSwitches[i];
267 
268  unsigned int numParameters = this->GetNumberOfParameters();
269 
270  // Compute jacobian parts
271  MatrixType zeroMatrix(numT2Signals, numDistributions, 0.0);
272 
273  m_SignalAttenuationsJacobian.resize(numParameters);
274  std::fill(m_SignalAttenuationsJacobian.begin(),m_SignalAttenuationsJacobian.end(),zeroMatrix);
275 
276  m_GramMatrix.set_size(numOnDistributions,numOnDistributions);
277  m_InverseGramMatrix.set_size(numOnDistributions,numOnDistributions);
278 
279  unsigned int posX = 0;
280  unsigned int posY = 0;
281  for (unsigned int i = 0;i < numDistributions;++i)
282  {
283  if (!m_CompartmentSwitches[i])
284  continue;
285 
286  m_GramMatrix(posX,posX) = m_CholeskyMatrix(i,i);
287  posY = posX + 1;
288  for (unsigned int j = i + 1;j < numDistributions;++j)
289  {
290  if (!m_CompartmentSwitches[j])
291  continue;
292 
293  m_GramMatrix(posX,posY) = m_CholeskyMatrix(i,j);
294  m_GramMatrix(posY,posX) = m_GramMatrix(posX,posY);
295  ++posY;
296  }
297 
298  ++posX;
299  }
300 
301  if (numOnDistributions > 0)
302  m_leCalculator->GetTensorPower(m_GramMatrix,m_InverseGramMatrix,-1.0);
303 
304  // Here gets F G^-1
305  m_FMatrixInverseG.set_size(numT2Signals,numOnDistributions);
306  for (unsigned int i = 0;i < numT2Signals;++i)
307  {
308  for (unsigned int j = 0;j < numOnDistributions;++j)
309  {
310  m_FMatrixInverseG(i,j) = 0.0;
311  unsigned int posK = 0;
312  for (unsigned int k = 0;k < numDistributions;++k)
313  {
314  if (!m_CompartmentSwitches[k])
315  continue;
316 
317  m_FMatrixInverseG(i,j) += m_PredictedSignalAttenuations(i,k) * m_InverseGramMatrix(posK,j);
318  ++posK;
319  }
320  }
321  }
322 
323  double b1Value = m_TestedParameters[0];
324 
327  B1GammaDerivativeDistributionIntegrand t2DerivativeIntegrand(m_T2SignalSimulator,epgVectors,epgDerivativeVectors);
328  t2DerivativeIntegrand.SetT1Value(m_T1Value);
329  t2DerivativeIntegrand.SetFlipAngle(b1Value);
330 
332 
333  for (unsigned int i = 0;i < numDistributions;++i)
334  {
335  t2DerivativeIntegrand.SetGammaMean(m_GammaMeans[i]);
336  t2DerivativeIntegrand.SetGammaVariance(m_GammaVariances[i]);
337  epgVectors.clear();
338  epgDerivativeVectors.clear();
339 
340  double minInterestZoneValue = std::max(0.0, m_GammaMeans[i] - 5.0 * std::sqrt(m_GammaVariances[i]));
341  double theoreticalMinValue = m_GammaMeans[i] - i * std::sqrt(m_GammaVariances[i]);
342  double maxInterestZoneValue = m_GammaMeans[i] + 5.0 * std::sqrt(m_GammaVariances[i]);
343  if (theoreticalMinValue < 0.0)
344  maxInterestZoneValue -= theoreticalMinValue;
345 
346  glQuad.SetInterestZone(minInterestZoneValue, maxInterestZoneValue);
347 
348  for (unsigned int j = 0;j < numT2Signals;++j)
349  {
350  t2DerivativeIntegrand.SetEchoNumber(j);
351  t2DerivativeIntegrand.SetB1DerivativeFlag(true);
352  // Handle B1 case
353  m_SignalAttenuationsJacobian[0](j,i) = glQuad.GetIntegralValue(t2DerivativeIntegrand);
354 
355  // Now handle parameters derivatives
356  if ((i == 1)||(!m_ConstrainedParameters))
357  {
358  t2DerivativeIntegrand.SetB1DerivativeFlag(false);
359  unsigned int index = 1 + (!m_ConstrainedParameters) * i;
360  m_SignalAttenuationsJacobian[index](j,i) = glQuad.GetIntegralValue(t2DerivativeIntegrand);
361  }
362  }
363  }
364 }
365 
366 } // end namespace anima
double GetIntegralValue(FunctionType integrand)
Computes the Gauss Laguerre quadrature of a function defined from R^+ into R. Recenters the function ...
virtual MeasureType GetValue(const ParametersType &parameters) const ITK_OVERRIDE
void PerformDecomposition()
Performs LDL decomposition from input matrix.
void SetInputMatrix(const MatrixType &matrix)
Set input matrix to decompose.
Integrand to compute the internal integral per distribution in B1GammaMixtureT2RelaxometryCostFunctio...
void SolveLinearLeastSquares() const
Computes maximum likelihood estimates of weights.
VectorType & SolveLinearSystem(const VectorType &b)
Solves linear system Ax=b and outputs result in new variable.
void SetInterestZone(double minVal, double maxVal)
Specifies region on which the main part of the function is to be seen. If not specified, R^+ is the region.
virtual void GetDerivative(const ParametersType &parameters, DerivativeType &derivative) const ITK_OVERRIDE
void SetNumberOfEchoes(unsigned int val)
std::map< double, anima::EPGSignalSimulator::RealVectorType > EPGVectorsMapType
Integrand to compute the internal derivative integral per distribution in B1GammaMixtureT2Relaxometry...