Xmipp  v3.23.11-Nereus
linear_system_helper.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  *
3  * Authors: David Strelak (davidstrelak@gmail.com)
4  *
5  * Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20  * 02111-1307 USA
21  *
22  * All comments concerning this program package may be sent to the
23  * e-mail address 'xmipp@cnb.csic.es'
24  ***************************************************************************/
25 
26 #include "linear_system_helper.h"
27 #include <random>
28 #include <algorithm>
29 #include <cassert>
30 
31 // Solve linear systems ---------------------------------------------------
32  // FIXME deprecated (use solveLinearSystem(WeightedLeastSquaresHelperMany &h, std::vector<Matrix1D<double>> &results))
34 {
35  Matrix2D<double> &A=h.A;
36  Matrix1D<double> &b=h.b;
37  Matrix2D<double> &AtA=h.AtA;
38  Matrix2D<double> &AtAinv=h.AtAinv;
39  Matrix1D<double> &Atb=h.Atb;
40 
41  // Compute AtA and Atb
42  int I=MAT_YSIZE(A);
43  int J=MAT_XSIZE(A);
44  AtA.initZeros(J,J);
45  Atb.initZeros(J);
46  for (int i=0; i<J; ++i)
47  {
48  for (int j=0; j<J; ++j)
49  {
50  double AtA_ij=0;
51  for (int k=0; k<I; ++k)
52  AtA_ij+=MAT_ELEM(A,k,i)*MAT_ELEM(A,k,j);
53  MAT_ELEM(AtA,i,j)=AtA_ij;
54  }
55  double Atb_i=0;
56  for (int k=0; k<I; ++k)
57  Atb_i+=MAT_ELEM(A,k,i)*VEC_ELEM(b,k);
58  VEC_ELEM(Atb,i)=Atb_i;
59  }
60 
61  // Compute the inverse of AtA
62  AtA.inv(AtAinv);
63 
64  // Now multiply by Atb
65  result.initZeros(J);
67  VEC_ELEM(result,i)+=MAT_ELEM(AtAinv,i,j)*VEC_ELEM(Atb,j);
68 }
69 
71 {
72  const size_t sizeI = h.A.mdimy;
73  const size_t sizeJ = h.A.mdimx;
74  h.AtA.resizeNoCopy(sizeJ, sizeJ);
75  h.Atb.resizeNoCopy(sizeJ);
76  // compute AtA
77  h.At = h.A.transpose();
78  // for each row of the output
79  for (size_t i = 0; i < sizeJ; ++i)
80  {
81  // for each column of the output (notice that we read row-wise from At)
82  for (size_t j = i; j < sizeJ; ++j)
83  {
84  double AtA_ij = 0;
85  // multiply elementwise row by row from At
86  for (size_t k = 0; k < sizeI; ++k) {
87  AtA_ij += h.At.mdata[i * sizeI + k] * h.At.mdata[j * sizeI + k];
88  }
89  // AtA is symmetric, so we don't need to iterate over everything
90  h.AtA.mdata[i * sizeJ + j] = h.AtA.mdata[j * sizeJ + i] = AtA_ij;
91  }
92  }
93  // Compute the inverse of AtA
94  h.AtA.inv(h.AtAinv); // AtAinv is also square matrix, same size as AtA
95 
96  assert(results.size() == h.bs.size());
97  auto res_it = results.begin();
98  auto b_it = h.bs.begin();
99  for (; res_it != results.end(); ++res_it, ++b_it) {
100  for (size_t i = 0; i < sizeJ; ++i)
101  {
102  double Atb_i = 0;
103  for (size_t k = 0; k < sizeI; ++k) {
104  Atb_i += h.A.mdata[k * sizeJ + i] * b_it->vdata[k];
105  }
106  h.Atb.vdata[i] = Atb_i;
107  }
108  // Now multiply by Atb
109  res_it->initZeros(sizeJ);
110  for (size_t i = 0; i < sizeJ; i++) {
111  for (size_t j = 0; j < sizeJ; j++) {
112  res_it->vdata[i] += h.AtAinv.mdata[i * sizeJ +j] * h.Atb.vdata[j];
113  }
114  }
115  }
116 }
117 
118 // Solve linear systems ---------------------------------------------------
120 {
121  Matrix2D<double> &A=h.A;
122  Matrix1D<double> &b=h.b;
123  Matrix1D<double> &w=h.w;
124 
125  // See http://en.wikipedia.org/wiki/Least_squares#Weighted_least_squares
127  {
128  double wii=sqrt(VEC_ELEM(w,i));
129  VEC_ELEM(b,i)*=wii;
130  for (size_t j=0; j<MAT_XSIZE(A); ++j)
131  MAT_ELEM(A,i,j)*=wii;
132  }
133  solveLinearSystem(h,result);
134 }
135 
137 {
138  const size_t sizeW = h.w.vdim;
139  h.w_sqrt.resizeNoCopy(h.w);
140  // compute weights
141  for(size_t i = 0; i < sizeW; ++i) {
142  h.w_sqrt.vdata[i] = sqrt(h.w.vdata[i]);
143  }
144  // update the matrix
145  const size_t sizeX = h.A.mdimx;
146  for(size_t i = 0; i < sizeW; ++i) {
147  const size_t offset = i * sizeX;
148  const auto v = h.w_sqrt[i];
149  for (size_t j = 0; j < sizeX; ++j)
150  h.A.mdata[offset + j] *= v;
151  }
152  // update values
153  for (auto &b : h.bs) {
154  for(size_t i = 0; i < sizeW; ++i) {
155  b.vdata[i] *= h.w_sqrt[i];
156  }
157  }
158  solveLinearSystem(h, results);
159 }
160 
161 // Solve linear system with RANSAC ----------------------------------------
162 //#define DEBUG
163 //#define DEBUG_MORE
165  double tol, int Niter, double outlierFraction)
166 {
167  int N=MAT_YSIZE(h.A); // Number of equations
168  int M=MAT_XSIZE(h.A); // Number of unknowns
169 
170  // Initialize a vector with all equation indexes
171  std::vector<int> eqIdx;
172  eqIdx.reserve(N);
173  for (int n=0; n<N; ++n)
174  eqIdx.push_back(n);
175  int *eqIdxPtr=&eqIdx[0];
176 
177 #ifdef DEBUG_MORE
178  // Show all equations
179  for (int n=0; n<N; n++)
180  {
181  std::cout << "Eq. " << n << " w=" << VEC_ELEM(h.w,n) << " b=" << VEC_ELEM(h.b,n) << " a=";
182  for (int j=0; j<M; j++)
183  std::cout << MAT_ELEM(h.A,n,j) << " ";
184  std::cout << std::endl;
185  }
186 #endif
187 
188  // Resize a WLS helper for solving the MxM equation systems
189  PseudoInverseHelper haux;
190  haux.A.resizeNoCopy(M,M);
191  haux.b.resizeNoCopy(M);
192  Matrix2D<double> &A=haux.A;
193  Matrix1D<double> &b=haux.b;
194 
195  // Solve Niter randomly chosen equation systems
196  double bestError=1e38;
197  const int Mdouble=M*sizeof(double);
198  int minNewM=(int)((1.0-outlierFraction)*N-M);
199  if (minNewM<0)
200  minNewM=0;
201 
202  Matrix1D<double> resultAux;
203  Matrix1D<int> idxIn(N);
205  for (int it=0; it<Niter; ++it)
206  {
207 #ifdef DEBUG_MORE
208  std::cout << "Iter: " << it << std::endl;
209 #endif
210  idxIn.initZeros();
211 
212  // Randomly select M equations
213  std::random_device rd;
214  std::mt19937 g(rd());
215  std::shuffle(eqIdx.begin(), eqIdx.end(), g);
216 
217  // Select the equation system
218  for (int i=0; i<M; ++i)
219  {
220  int idx=eqIdxPtr[i];
221  memcpy(&MAT_ELEM(A,i,0),&MAT_ELEM(h.A,idx,0),Mdouble);
222  VEC_ELEM(b,i)=VEC_ELEM(h.b,idx);
223  VEC_ELEM(idxIn,idx)=1;
224 #ifdef DEBUG_MORE
225  std::cout << " Using Eq.: " << idx << " for first solution" << std::endl;
226 #endif
227  }
228 
229  // Solve the equation system
230  // We use LS because the weight of some of the equations might be too low
231  // and then the system is ill conditioned
232  solveLinearSystem(haux, resultAux);
233 
234  // Study the residuals of the rest
235  int newM=0;
236  for (int i=M+1; i<N; ++i)
237  {
238  int idx=eqIdxPtr[i];
239  double bp=0;
240  for (int j=0; j<M; ++j)
241  bp+=MAT_ELEM(h.A,idx,j)*VEC_ELEM(resultAux,j);
242  if (fabs(bp-VEC_ELEM(h.b,idx))<tol)
243  {
244  VEC_ELEM(idxIn,idx)=1;
245  ++newM;
246  }
247 #ifdef DEBUG_MORE
248  std::cout << " Checking Eq.: " << idx << " err=" << bp-VEC_ELEM(h.b,idx) << std::endl;
249 #endif
250  }
251 
252  // If the model represent more points
253  if (newM>minNewM)
254  {
255  Matrix2D<double> &A2=haux2.A;
256  Matrix1D<double> &b2=haux2.b;
257  Matrix1D<double> &w2=haux2.w;
258  A2.resizeNoCopy(M+newM,M);
259  b2.resizeNoCopy(M+newM);
260  w2.resizeNoCopy(M+newM);
261 
262  // Select the equation system
263  int targeti=0;
264  for (int i=0; i<N; ++i)
265  if (VEC_ELEM(idxIn,i))
266  {
267  memcpy(&MAT_ELEM(A2,targeti,0),&MAT_ELEM(h.A,i,0),Mdouble);
268  VEC_ELEM(b2,targeti)=VEC_ELEM(h.b,i);
269  VEC_ELEM(w2,targeti)=VEC_ELEM(h.w,i);
270  ++targeti;
271  }
272 
273  // Solve it with WLS
274  weightedLeastSquares(haux2, resultAux);
275 
276  // Compute the mean error
277  double err=0;
278  for (int i=0; i<M+newM; ++i)
279  {
280  double bp=0;
281  for (int j=0; j<M; ++j)
282  bp+=MAT_ELEM(A2,i,j)*VEC_ELEM(resultAux,j);
283  err+=fabs(VEC_ELEM(b2,i)-bp)*VEC_ELEM(w2,i);
284  }
285  err/=(M+newM);
286  if (err<bestError)
287  {
288  bestError=err;
289  result=resultAux;
290 #ifdef DEBUG
291  std::cout << "Best solution iter: " << it << " Error=" << err << " frac=" << (float)(M+newM)/VEC_XSIZE(h.b) << std::endl;
292 #ifdef DEBUG_MORE
293  std::cout << "Result:" << result << std::endl;
294  for (int i=0; i<M+newM; ++i)
295  {
296  double bp=0;
297  for (int j=0; j<M; ++j)
298  bp+=MAT_ELEM(A2,i,j)*VEC_ELEM(resultAux,j);
299  std::cout << "Eq. " << i << " w=" << VEC_ELEM(w2,i) << " b2=" << VEC_ELEM(b2,i) << " bp=" << bp << std::endl;
300  err+=fabs(VEC_ELEM(b2,i)-bp)*VEC_ELEM(w2,i);
301  }
302 #endif
303 #endif
304  }
305  }
306  }
307  return bestError;
308 }
309 #undef DEBUG
310 
312  // Input
315  double tol;
316  int Niter;
318 
319  // Output
321  double error;
322 };
323 
325 {
326  ThreadRansacArgs * master = (ThreadRansacArgs *) args;
327  master->error=ransacWeightedLeastSquaresBasic(*(master->h), master->result,
328  master->tol, master->Niter, master->outlierFraction);
329  return NULL;
330 }
331 
333  double tol, int Niter, double outlierFraction, int Nthreads)
334 {
335  // Read and preprocess the images
336  pthread_t * th_ids = new pthread_t[Nthreads];
337  ThreadRansacArgs * th_args = new ThreadRansacArgs[Nthreads];
338  for (int nt = 0; nt < Nthreads; nt++) {
339  // Passing parameters to each thread
340  th_args[nt].myThreadID = nt;
341  th_args[nt].h = &h;
342  th_args[nt].tol = tol;
343  th_args[nt].Niter = Niter/Nthreads;
344  th_args[nt].outlierFraction = outlierFraction;
345  pthread_create((th_ids + nt), NULL, threadRansacWeightedLeastSquares,
346  (void *) (th_args + nt));
347  }
348 
349  // Waiting for threads to finish
350  double err=1e38;
351  for (int nt = 0; nt < Nthreads; nt++)
352  {
353  pthread_join(*(th_ids + nt), NULL);
354  if (th_args[nt].error<err)
355  {
356  err=th_args[nt].error;
357  result=th_args[nt].result;
358  }
359  }
360 
361  // Threads structures are not needed any more
362  delete []th_ids;
363  delete []th_args;
364 }
#define FOR_ALL_ELEMENTS_IN_MATRIX2D(m)
Definition: matrix2d.h:104
#define MAT_YSIZE(m)
Definition: matrix2d.h:124
#define VEC_ELEM(v, i)
Definition: matrix1d.h:245
#define VEC_XSIZE(m)
Definition: matrix1d.h:77
doublereal * g
void sqrt(Image< double > &op)
void inv(Matrix2D< T > &result) const
Definition: matrix2d.cpp:663
doublereal * w
T * mdata
Definition: matrix2d.h:395
Matrix2D< double > AtAinv
Matrix1D< double > Atb
Matrix1D< double > result
Matrix2D< T > transpose() const
Definition: matrix2d.cpp:1314
std::vector< Matrix1D< double > > bs
#define i
ql0001_ & k(htemp+1),(cvec+1),(atemp+1),(bj+1),(bl+1),(bu+1),(x+1),(clamda+1), &iout, infoqp, &zero,(w+1), &lenw,(iw+1), &leniw, &glob_grd.epsmac
void resizeNoCopy(int Ydim, int Xdim)
Definition: matrix2d.h:534
#define MAT_ELEM(m, i, j)
Definition: matrix2d.h:116
#define FOR_ALL_ELEMENTS_IN_MATRIX1D(v)
Definition: matrix1d.h:72
doublereal * b
Matrix1D< double > b
Matrix2D< double > A
size_t mdimy
Definition: matrix2d.h:413
void initZeros()
Definition: matrix1d.h:592
void solveLinearSystem(PseudoInverseHelper &h, Matrix1D< double > &result)
#define j
WeightedLeastSquaresHelper * h
Matrix2D< double > AtA
void ransacWeightedLeastSquares(WeightedLeastSquaresHelper &h, Matrix1D< double > &result, double tol, int Niter, double outlierFraction, int Nthreads)
#define MAT_XSIZE(m)
Definition: matrix2d.h:120
void initZeros()
Definition: matrix2d.h:626
void resizeNoCopy(int Xdim)
Definition: matrix1d.h:458
size_t mdimx
Definition: matrix2d.h:410
void * threadRansacWeightedLeastSquares(void *args)
T * vdata
The array itself.
Definition: matrix1d.h:258
double ransacWeightedLeastSquaresBasic(WeightedLeastSquaresHelper &h, Matrix1D< double > &result, double tol, int Niter, double outlierFraction)
void weightedLeastSquares(WeightedLeastSquaresHelper &h, Matrix1D< double > &result)
int * n
size_t vdim
Number of elements.
Definition: matrix1d.h:264