Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
mitkGeneralizedLinearModel.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
14 
15 #include <mitkDistModels.h>
16 #include <mitkLinkModels.h>
17 
18 #include <v3p_netlib.h>
19 #include <vnl/algo/vnl_qr.h>
20 #include <mitkLogMacros.h>
21 #include <algorithm>
22 #include <limits>
23 
24 static void _UpdateXMatrix(const vnl_matrix<double> &xData, bool addConstant, v3p_netlib_doublereal *x);
25 static void _UpdatePermXMatrix(const vnl_matrix<double> &xData, bool addConstant, const vnl_vector<unsigned int> &permutation, vnl_matrix<double> &x);
26 static void _InitMuEta(mitk::DistSimpleBinominal *dist, mitk::LogItLinking *link, const vnl_vector<double> &yData, vnl_vector<double> &mu, vnl_vector<double> &eta);
27 static void _FinalizeBVector(vnl_vector<double> &b, vnl_vector<unsigned int> &perm, int cols);
28 
29 
30 double mitk::GeneralizedLinearModel::Predict( const vnl_vector<double> &c)
31 {
32  LogItLinking link;
33  double mu = 0;
34  int cols = m_B.size();
35  for (int i = 0; i < cols; ++i)
36  {
37  if (!m_AddConstantColumn)
38  mu += c(i)* m_B(i);
39  else if ( i == 0)
40  mu += 1* m_B(i);
41  else
42  mu += c(i-1)*m_B(i);
43  }
44  return link.InverseLink(mu);
45 }
46 
47 vnl_vector<double> mitk::GeneralizedLinearModel::Predict(const vnl_matrix<double> &x)
48 {
49  LogItLinking link;
50  vnl_vector<double> mu(x.rows());
51  int cols = m_B.size();
52  for (unsigned int r = 0 ; r < mu.size(); ++r)
53  {
54  mu(r) = 0;
55  for (int c = 0; c < cols; ++c)
56  {
57  if (!m_AddConstantColumn)
58  mu(r) += x(r,c)*m_B(c);
59  else if ( c == 0)
60  mu(r) += m_B(c);
61  else
62  mu(r) += x(r,c-1)*m_B(c);
63  }
64  mu(r) = link.InverseLink(mu(r));
65  }
66  return mu;
67 }
68 
69 vnl_vector<double> mitk::GeneralizedLinearModel::B()
70 {
71  return m_B;
72 }
73 
74 vnl_vector<double> mitk::GeneralizedLinearModel::ExpMu(const vnl_matrix<double> &x)
75 {
76  LogItLinking link;
77  vnl_vector<double> mu(x.rows());
78  int cols = m_B.size();
79  for (unsigned int r = 0 ; r < mu.size(); ++r)
80  {
81  mu(r) = 0;
82  for (int c = 0; c < cols; ++c)
83  {
84  if (!m_AddConstantColumn)
85  mu(r) += x(r,c)*m_B(c);
86  else if ( c == 0)
87  mu(r) += m_B(c);
88  else
89  mu(r) += x(r,c-1)*m_B(c);
90  }
91  mu(r) = exp(-mu(r));
92  }
93  return mu;
94 }
95 
96 mitk::GeneralizedLinearModel::GeneralizedLinearModel(const vnl_matrix<double> &xData, const vnl_vector<double> &yData, bool addConstantColumn) :
97  m_AddConstantColumn(addConstantColumn)
98 {
99  EstimatePermutation(xData);
100 
101  DistSimpleBinominal dist;
102  LogItLinking link;
103  vnl_matrix<double> x;
104  int rows = xData.rows();
105  int cols = m_Permutation.size();
106  vnl_vector<double> mu(rows);
107  vnl_vector<double> eta(rows);
108  vnl_vector<double> weightedY(rows);
109  vnl_matrix<double> weightedX(rows, cols);
110  vnl_vector<double> oldB(cols);
111 
112  _UpdatePermXMatrix(xData, m_AddConstantColumn, m_Permutation, x);
113  _InitMuEta(&dist, &link, yData, mu, eta);
114 
115  int iter = 0;
116  int iterLimit = 100;
117  double sqrtEps = sqrt(std::numeric_limits<double>::epsilon());
118  double convertCriterion =1e-6;
119 
120  m_B.set_size(m_Permutation.size());
121  m_B.fill(0);
122 
123  while (iter <= iterLimit)
124  {
125  ++iter;
126 
127  oldB = m_B;
128  // Do Row-wise operation. No Vector operation at this point.
129  for (int r = 0; r<rows; ++r)
130  {
131  double deta = link.DLink(mu(r));
132  double zBuffer = eta(r) + (yData(r) - mu(r))*deta;
133  double sqrtWeight = 1 / (std::abs(deta) * dist.SqrtVariance(mu(r)));
134 
135  weightedY(r) = zBuffer * sqrtWeight;
136  for (int c=0; c<cols; ++c)
137  {
138  weightedX(r,c) = x(r,c) * sqrtWeight;
139  }
140  }
141  vnl_qr<double> qr(weightedX);
142  m_B = qr.solve(weightedY);
143  eta = x * m_B;
144  for (int r = 0; r < rows; ++r)
145  {
146  mu(r) = link.InverseLink(eta(r));
147  }
148 
149  bool stayInLoop = false;
150  for(int c= 0; c < cols; ++c)
151  {
152  stayInLoop |= std::abs( m_B(c) - oldB(c)) > convertCriterion * std::max(sqrtEps, std::abs(oldB(c)));
153  }
154  if (!stayInLoop)
155  break;
156  }
157  _FinalizeBVector(m_B, m_Permutation, xData.cols());
158 }
159 
160 void mitk::GeneralizedLinearModel::EstimatePermutation(const vnl_matrix<double> &xData)
161 {
162  v3p_netlib_integer rows = xData.rows();
163  v3p_netlib_integer cols = xData.cols();
164 
165  if (m_AddConstantColumn)
166  ++cols;
167 
168  v3p_netlib_doublereal *x = new v3p_netlib_doublereal[rows* cols];
169  _UpdateXMatrix(xData, m_AddConstantColumn, x);
170  v3p_netlib_doublereal *qraux = new v3p_netlib_doublereal[cols];
171  v3p_netlib_integer *jpvt = new v3p_netlib_integer[cols];
172  std::fill_n(jpvt,cols,0);
173  v3p_netlib_doublereal *work = new v3p_netlib_doublereal[cols];
174  std::fill_n(work,cols,0);
175  v3p_netlib_integer job = 16;
176 
177  // Make a call to Lapack-DQRDC which does QR with permutation
178  // Permutation is saved in JPVT.
179  v3p_netlib_dqrdc_(x, &rows, &rows, &cols, qraux, jpvt, work, &job);
180 
181  double limit = std::abs(x[0]) * std::max(cols, rows) * std::numeric_limits<double>::epsilon();
182  // Calculate the rank of the matrix
183  int m_Rank = 0;
184  for (int i = 0; i <cols; ++i)
185  {
186  m_Rank += (std::abs(x[i*rows + i]) > limit) ? 1 : 0;
187  }
188  // Create a permutation vector
189  m_Permutation.set_size(m_Rank);
190  for (int i = 0; i < m_Rank; ++i)
191  {
192  m_Permutation(i) = jpvt[i]-1;
193  }
194 
195  delete[] x;
196  delete[] qraux;
197  delete[] jpvt;
198  delete[] work;
199 }
200 
201 // Copy a vnl-matrix to an c-array with row-wise representation.
202 // Adds a constant column if required.
203 static void _UpdateXMatrix(const vnl_matrix<double> &xData, bool addConstant, v3p_netlib_doublereal *x)
204 {
205  v3p_netlib_integer rows = xData.rows();
206  v3p_netlib_integer cols = xData.cols();
207  if (addConstant)
208  ++cols;
209 
210  for (int r=0; r < rows; ++r)
211  {
212  for (int c=0; c <cols; ++c)
213  {
214  if (!addConstant)
215  {
216  x[c*rows + r] = xData(r,c);
217  } else if (c == 0)
218  {
219  x[c*rows + r] = 1.0;
220  } else
221  {
222  x[c*rows + r] = xData(r, c-1);
223  }
224  }
225  }
226 }
227 
228 // Fills the value of the xData-matrix into the x-matrix. Adds a constant
229 // column if required. Permutes the rows corresponding to the permutation vector.
230 static void _UpdatePermXMatrix(const vnl_matrix<double> &xData, bool addConstant, const vnl_vector<unsigned int> &permutation, vnl_matrix<double> &x)
231 {
232  int rows = xData.rows();
233  int cols = permutation.size();
234  x.set_size(rows, cols);
235  for (int r=0; r < rows; ++r)
236  {
237  for (int c=0; c<cols; ++c)
238  {
239  unsigned int newCol = permutation(c);
240  if (!addConstant)
241  {
242  x(r, c) = xData(r,newCol);
243  } else if (newCol == 0)
244  {
245  x(r, c) = 1.0;
246  } else
247  {
248  x(r, c) = xData(r, newCol-1);
249  }
250  }
251  }
252 }
253 
254 // Initialize mu and eta by calling the corresponding
255 // link and distribution functions on each row
256 static void _InitMuEta(mitk::DistSimpleBinominal *dist, mitk::LogItLinking *link, const vnl_vector<double> &yData, vnl_vector<double> &mu, vnl_vector<double> &eta)
257 {
258  int rows = yData.size();
259  mu.set_size(rows);
260  eta.set_size(rows);
261  for (int r = 0; r < rows; ++r)
262  {
263  mu(r) = dist->Init(yData(r));
264  eta(r) = link->Link(mu(r));
265  }
266 }
267 
268 // Inverts the permutation on a given b-vector.
269 // Necessary to get a b-vector that match the original data
270 static void _FinalizeBVector(vnl_vector<double> &b, vnl_vector<unsigned int> &perm, int cols)
271 {
272  vnl_vector<double> tempB(cols+1);
273  tempB.fill(0);
274  for (unsigned int c = 0; c < perm.size(); ++c)
275  {
276  tempB(perm(c)) = b(c);
277  }
278  b = tempB;
279 }
static void _UpdatePermXMatrix(const vnl_matrix< double > &xData, bool addConstant, const vnl_vector< unsigned int > &permutation, vnl_matrix< double > &x)
double Predict(const vnl_vector< double > &c)
Predicts the value corresponding to the given vector.
double InverseLink(double eta) override
GeneralizedLinearModel(const vnl_matrix< double > &xData, const vnl_vector< double > &yData, bool addConstantColumn=true)
Initialization of the GLM. The parameters needs to be passed at the beginning.
static T max(T x, T y)
Definition: svm.cpp:56
double Link(double mu) override
static void _InitMuEta(mitk::DistSimpleBinominal *dist, mitk::LogItLinking *link, const vnl_vector< double > &yData, vnl_vector< double > &mu, vnl_vector< double > &eta)
vnl_vector< double > ExpMu(const vnl_matrix< double > &x)
Estimation of the exponential factor for a given function.
static void _UpdateXMatrix(const vnl_matrix< double > &xData, bool addConstant, v3p_netlib_doublereal *x)
static void _FinalizeBVector(vnl_vector< double > &b, vnl_vector< unsigned int > &perm, int cols)
double SqrtVariance(double mu) override
vnl_vector< double > B()
Returns the b-Vector for the estimation.
double DLink(double mu) override
double Init(double y) override