ANIMA  4.0
animaNNLSOptimizer.cxx
Go to the documentation of this file.
1 #include <animaNNLSOptimizer.h>
2 #include <vnl/algo/vnl_qr.h>
3 
4 namespace anima
5 {
6 
7 const double NNLSOptimizer::m_EpsilonValue = 1.0e-12;
8 
10 {
11  unsigned int parametersSize = m_DataMatrix.cols();
12  unsigned int numEquations = m_DataMatrix.rows();
13 
14  if ((numEquations != m_Points.size())||(numEquations == 0)||(parametersSize == 0))
15  itkExceptionMacro("Wrongly sized inputs to NNLS, aborting");
16 
17  m_CurrentPosition.SetSize(parametersSize);
18  m_CurrentPosition.Fill(0.0);
19  m_TreatedIndexes.resize(parametersSize);
20  std::fill(m_TreatedIndexes.begin(), m_TreatedIndexes.end(),0);
21  m_ProcessedIndexes.clear();
22  m_WVector.resize(parametersSize);
23 
24  unsigned int numProcessedIndexes = 0;
25 
26  this->ComputeWVector();
27 
28  bool continueMainLoop = true;
29  double previousMaxW = -1;
30  while (continueMainLoop)
31  {
32  double maxW = 0;
33  int maxIndex = -1;
34  for (unsigned int i = 0;i < parametersSize;++i)
35  {
36  if (m_TreatedIndexes[i] == 0)
37  {
38  if (maxIndex < 0)
39  {
40  maxW = m_WVector[i];
41  maxIndex = i;
42  }
43  else if (maxW < m_WVector[i])
44  {
45  maxW = m_WVector[i];
46  maxIndex = i;
47  }
48  }
49  }
50 
51  if (maxW <= m_EpsilonValue)
52  {
53  continueMainLoop = false;
54  continue;
55  }
56 
57  previousMaxW = maxW;
58  m_TreatedIndexes[maxIndex] = 1;
59  m_ProcessedIndexes.push_back(maxIndex);
60  numProcessedIndexes = m_ProcessedIndexes.size();
61  this->ComputeSPVector();
62 
63  double minSP = m_SPVector[0];
64  for (unsigned int i = 1;i < numProcessedIndexes;++i)
65  {
66  if (m_SPVector[i] < minSP)
67  minSP = m_SPVector[i];
68  }
69 
70  // Starting inner loop
71  while (minSP <= 0)
72  {
73  bool foundOne = false;
74  double alpha = 0;
75  double indexSelected = 0;
76  for (unsigned int i = 0;i < numProcessedIndexes;++i)
77  {
78  if (m_SPVector[i] > 0)
79  continue;
80 
81  double tmpAlpha = m_CurrentPosition[m_ProcessedIndexes[i]] / (m_CurrentPosition[m_ProcessedIndexes[i]] - m_SPVector[i] + m_EpsilonValue);
82  if ((tmpAlpha < alpha)||(!foundOne))
83  {
84  alpha = tmpAlpha;
85  indexSelected = m_ProcessedIndexes[i];
86  foundOne = true;
87  }
88  }
89 
90  for (unsigned int i = 0;i < parametersSize;++i)
91  m_CurrentPosition[i] -= alpha * m_CurrentPosition[i];
92 
93  // Update positions
94  for (unsigned int i = 0;i < numProcessedIndexes;++i)
95  {
96  m_CurrentPosition[m_ProcessedIndexes[i]] += alpha * m_SPVector[i];
97 
98  if (std::abs(m_CurrentPosition[m_ProcessedIndexes[i]]) <= m_EpsilonValue)
99  m_TreatedIndexes[m_ProcessedIndexes[i]] = 0;
100  }
101 
102  m_CurrentPosition[indexSelected] = 0;
103  m_TreatedIndexes[indexSelected] = 0;
104 
105  // Update processed indexes
106  numProcessedIndexes = this->UpdateProcessedIndexes();
107  if (numProcessedIndexes == 0)
108  break;
109 
110  this->ComputeSPVector();
111 
112  minSP = m_SPVector[0];
113  for (unsigned int i = 1;i < numProcessedIndexes;++i)
114  {
115  if (m_SPVector[i] < minSP)
116  minSP = m_SPVector[i];
117  }
118  }
119 
120  m_CurrentPosition.Fill(0);
121  for (unsigned int i = 0;i < numProcessedIndexes;++i)
122  m_CurrentPosition[m_ProcessedIndexes[i]] = m_SPVector[i];
123 
124  if (numProcessedIndexes == parametersSize)
125  {
126  continueMainLoop = false;
127  continue;
128  }
129 
130  this->ComputeWVector();
131  }
132 }
133 
134 void NNLSOptimizer::ComputeWVector()
135 {
136  unsigned int parametersSize = m_DataMatrix.cols();
137  unsigned int numEquations = m_DataMatrix.rows();
138 
139  m_WVector.resize(parametersSize);
140 
141  std::fill(m_WVector.begin(),m_WVector.end(),0.0);
142  if (!m_SquaredProblem)
143  {
144  for (unsigned int i = 0;i < numEquations;++i)
145  {
146  double tmpValue = m_Points[i];
147  for (unsigned int j = 0;j < parametersSize;++j)
148  tmpValue -= m_DataMatrix.get(i,j) * m_CurrentPosition[j];
149 
150  for (unsigned int j = 0;j < parametersSize;++j)
151  m_WVector[j] += m_DataMatrix.get(i,j) * tmpValue;
152  }
153  }
154  else
155  {
156  for (unsigned int i = 0;i < numEquations;++i)
157  {
158  m_WVector[i] = m_Points[i];
159  for (unsigned int j = 0;j < parametersSize;++j)
160  m_WVector[i] -= m_DataMatrix.get(i,j) * m_CurrentPosition[j];
161  }
162  }
163 }
164 
165 unsigned int NNLSOptimizer::UpdateProcessedIndexes()
166 {
167  unsigned int numProcessedIndexes = 0;
168  unsigned int parametersSize = m_DataMatrix.cols();
169 
170  for (unsigned int i = 0;i < parametersSize;++i)
171  numProcessedIndexes += m_TreatedIndexes[i];
172 
173  m_ProcessedIndexes.resize(numProcessedIndexes);
174  unsigned int pos = 0;
175  for (unsigned int i = 0;i < parametersSize;++i)
176  {
177  if (m_TreatedIndexes[i] != 0)
178  {
179  m_ProcessedIndexes[pos] = i;
180  ++pos;
181  }
182  }
183 
184  return numProcessedIndexes;
185 }
186 
187 void NNLSOptimizer::ComputeSPVector()
188 {
189  unsigned int numEquations = m_DataMatrix.rows();
190  unsigned int numProcessedIndexes = m_ProcessedIndexes.size();
191 
192  if (!m_SquaredProblem)
193  {
194  m_DataMatrixP.set_size(numEquations,numProcessedIndexes);
195  m_SPVector.set_size(numProcessedIndexes);
196  m_SPVector.fill(0.0);
197 
198  // We do not square on the fly since we are not allowed to, squared matrix has a chance of being too bad
199  for (unsigned int i = 0;i < numEquations;++i)
200  {
201  for (unsigned int j = 0;j < numProcessedIndexes;++j)
202  m_DataMatrixP.put(i,j,m_DataMatrix.get(i,m_ProcessedIndexes[j]));
203  }
204 
205  m_SPVector = vnl_qr <double> (m_DataMatrixP).solve(m_Points);
206  }
207  else
208  {
209  m_DataMatrixP.set_size(numProcessedIndexes,numProcessedIndexes);
210  m_SPVector.set_size(numProcessedIndexes);
211  m_DataMatrixP.fill(0.0);
212  m_SPVector.fill(0.0);
213 
214  for (unsigned int i = 0;i < numProcessedIndexes;++i)
215  {
216  unsigned int iIndex = m_ProcessedIndexes[i];
217  m_SPVector[i] = m_Points[iIndex];
218  for (unsigned int j = i;j < numProcessedIndexes;++j)
219  {
220  unsigned int jIndex = m_ProcessedIndexes[j];
221  m_DataMatrixP.put(i,j,m_DataMatrix.get(iIndex,jIndex));
222 
223  if (i != j)
224  m_DataMatrixP.put(j,i,m_DataMatrixP.get(i,j));
225  }
226  }
227 
228  m_CholeskySolver.SetInputMatrix(m_DataMatrixP);
229  m_CholeskySolver.PerformDecomposition();
230  m_CholeskySolver.SolveLinearSystemInPlace(m_SPVector);
231  }
232 }
233 
235 {
236  double residualValue = 0;
237 
238  for (unsigned int i = 0;i < m_DataMatrix.rows();++i)
239  {
240  double tmpVal = 0;
241  for (unsigned int j = 0;j < m_DataMatrix.cols();++j)
242  tmpVal += m_DataMatrix.get(i,j) * m_CurrentPosition[j];
243 
244  residualValue += (tmpVal - m_Points[i]) * (tmpVal - m_Points[i]);
245  }
246 
247  return residualValue;
248 }
249 
250 }
void PerformDecomposition()
Performs LDL decomposition from input matrix.
void SetInputMatrix(const MatrixType &matrix)
Set input matrix to decompose.
void SolveLinearSystemInPlace(VectorType &b)
Solves linear system Ax=b and outputs result in input b variable.
void StartOptimization() ITK_OVERRIDE