Xmipp  v3.23.11-Nereus
Classes | Functions
linear_system_helper.cpp File Reference
#include "linear_system_helper.h"
#include <random>
#include <algorithm>
#include <cassert>
Include dependency graph for linear_system_helper.cpp:

Go to the source code of this file.

Classes

struct  ThreadRansacArgs
 

Functions

void solveLinearSystem (PseudoInverseHelper &h, Matrix1D< double > &result)
 
void solveLinearSystem (WeightedLeastSquaresHelperMany &h, std::vector< Matrix1D< double >> &results)
 
void weightedLeastSquares (WeightedLeastSquaresHelper &h, Matrix1D< double > &result)
 
void weightedLeastSquares (WeightedLeastSquaresHelperMany &h, std::vector< Matrix1D< double >> &results)
 
double ransacWeightedLeastSquaresBasic (WeightedLeastSquaresHelper &h, Matrix1D< double > &result, double tol, int Niter, double outlierFraction)
 
void * threadRansacWeightedLeastSquares (void *args)
 
void ransacWeightedLeastSquares (WeightedLeastSquaresHelper &h, Matrix1D< double > &result, double tol, int Niter, double outlierFraction, int Nthreads)
 

Function Documentation

◆ ransacWeightedLeastSquares()

void ransacWeightedLeastSquares ( WeightedLeastSquaresHelper h,
Matrix1D< double > &  result,
double  tol,
int  Niter = 10000,
double  outlierFraction = 0.25,
int  Nthreads = 1 
)

Solve Weighted least square problem Ax=b and weights w, with RANSAC. Tol is a tolerance value: if an equation is fulfilled with an error smaller than tol, then it is considered to fulfill the model. Niter is the number of RANSAC iterations to perform. The outlier fraction is the fraction of equations that, at maximum, can be considered as outliers.

Definition at line 332 of file linear_system_helper.cpp.

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 }
Matrix1D< double > result
WeightedLeastSquaresHelper * h
void * threadRansacWeightedLeastSquares(void *args)

◆ ransacWeightedLeastSquaresBasic()

double ransacWeightedLeastSquaresBasic ( WeightedLeastSquaresHelper h,
Matrix1D< double > &  result,
double  tol,
int  Niter,
double  outlierFraction 
)

Definition at line 164 of file linear_system_helper.cpp.

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 }
#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
#define i
void resizeNoCopy(int Ydim, int Xdim)
Definition: matrix2d.h:534
#define MAT_ELEM(m, i, j)
Definition: matrix2d.h:116
doublereal * b
Matrix1D< double > b
Matrix2D< double > A
void solveLinearSystem(PseudoInverseHelper &h, Matrix1D< double > &result)
#define j
#define MAT_XSIZE(m)
Definition: matrix2d.h:120
void resizeNoCopy(int Xdim)
Definition: matrix1d.h:458
void weightedLeastSquares(WeightedLeastSquaresHelper &h, Matrix1D< double > &result)
int * n

◆ solveLinearSystem() [1/2]

void solveLinearSystem ( PseudoInverseHelper h,
Matrix1D< double > &  result 
)

Solve Linear system Ax=b with pseudoinverse. A and b must be set inside the PseudoInverseHelper, the rest of the fields in PseudoInverseHelper are used by this routine to avoid several allocation/deallocations DEPRECATED, use solveLinearSystem(WeightedLeastSquaresHelperMany &h, std::vector<Matrix1D<double>> &results)

Definition at line 33 of file linear_system_helper.cpp.

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 }
#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
void inv(Matrix2D< T > &result) const
Definition: matrix2d.cpp:663
Matrix2D< double > AtAinv
Matrix1D< double > Atb
#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
#define MAT_ELEM(m, i, j)
Definition: matrix2d.h:116
doublereal * b
Matrix1D< double > b
Matrix2D< double > A
void initZeros()
Definition: matrix1d.h:592
#define j
Matrix2D< double > AtA
#define MAT_XSIZE(m)
Definition: matrix2d.h:120
void initZeros()
Definition: matrix2d.h:626

◆ solveLinearSystem() [2/2]

void solveLinearSystem ( WeightedLeastSquaresHelperMany h,
std::vector< Matrix1D< double >> &  result 
)

Solve Linear system Ax=[b] with pseudoinverse. A and all 'b's must be set inside the helper

Definition at line 70 of file linear_system_helper.cpp.

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 }
void inv(Matrix2D< T > &result) const
Definition: matrix2d.cpp:663
T * mdata
Definition: matrix2d.h:395
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
size_t mdimy
Definition: matrix2d.h:413
#define j
void resizeNoCopy(int Xdim)
Definition: matrix1d.h:458
size_t mdimx
Definition: matrix2d.h:410
T * vdata
The array itself.
Definition: matrix1d.h:258

◆ threadRansacWeightedLeastSquares()

void* threadRansacWeightedLeastSquares ( void *  args)

Definition at line 324 of file linear_system_helper.cpp.

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 }
Matrix1D< double > result
WeightedLeastSquaresHelper * h
double ransacWeightedLeastSquaresBasic(WeightedLeastSquaresHelper &h, Matrix1D< double > &result, double tol, int Niter, double outlierFraction)

◆ weightedLeastSquares() [1/2]

void weightedLeastSquares ( WeightedLeastSquaresHelper h,
Matrix1D< double > &  result 
)

Solve Weighted least square problem Ax=b with pseudoinverse and weights w. A, w and b must be set inside the WeightedLeastSquaresHelper, the rest of the fields in WeightedLeastSquaresHelper are used by this routine to avoid several allocation/deallocations.

The normal equations of this problem are A^t W A x = A^t W b, where W is a diagonal matrix whose entries are in the vector w.

DEPRECATED, use weightedLeastSquares(WeightedLeastSquaresHelperMany &h, std::vector<Matrix1D<double>> &results)

Definition at line 119 of file linear_system_helper.cpp.

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 }
#define VEC_ELEM(v, i)
Definition: matrix1d.h:245
void sqrt(Image< double > &op)
doublereal * w
#define i
#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
void solveLinearSystem(PseudoInverseHelper &h, Matrix1D< double > &result)
#define j
#define MAT_XSIZE(m)
Definition: matrix2d.h:120

◆ weightedLeastSquares() [2/2]

void weightedLeastSquares ( WeightedLeastSquaresHelperMany h,
std::vector< Matrix1D< double >> &  results 
)

Solve Weighted least square problem Ax=b with pseudoinverse and weights w for multiple b. A, w and all 'b's must be set inside the helper to avoid several allocation/deallocations.

The normal equations of this problem are A^t W A x = A^t W b, where W is a diagonal matrix whose entries are in the vector w.

Definition at line 136 of file linear_system_helper.cpp.

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 }
void sqrt(Image< double > &op)
T * mdata
Definition: matrix2d.h:395
std::vector< Matrix1D< double > > bs
#define i
doublereal * b
void solveLinearSystem(PseudoInverseHelper &h, Matrix1D< double > &result)
#define j
void resizeNoCopy(int Xdim)
Definition: matrix1d.h:458
size_t mdimx
Definition: matrix2d.h:410
T * vdata
The array itself.
Definition: matrix1d.h:258
size_t vdim
Number of elements.
Definition: matrix1d.h:264