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