Xmipp  v3.23.11-Nereus
ml2d.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  *
3  * Authors: Sjors Scheres scheres@cnb.csic.es (2007)
4  *
5  * Unidad de Bioinformatica del 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.uam.es'
24  ***************************************************************************/
25 
26 #include <algorithm>
27 #include <iomanip>
28 #include "ml2d.h"
29 
31 {
32  do_ML3D = false;
33  refs_per_class = 1;
34  defaultNiter = 100;
35  defaultRoot = "ml2d";
38  defaultNiter = 100;
39  blocks = 1;
40  factor_nref = 1;
41  outRefsMd = "";
42 }
43 
45 {
46  // Set sampling stuff: flipping matrices, psi_step etc.
47  Matrix2D<double> A(3, 3);
48  psi_max = 90.;
52  // 0, 90, 180 & 270 degree flipping, as well as mirror
53  A.initIdentity();
54  F.push_back(A);
55  A(0, 0) = 0.;
56  A(1, 1) = 0.;
57  A(1, 0) = 1.;
58  A(0, 1) = -1;
59  F.push_back(A);
60  A(0, 0) = -1.;
61  A(1, 1) = -1.;
62  A(1, 0) = 0.;
63  A(0, 1) = 0;
64  F.push_back(A);
65  A(0, 0) = 0.;
66  A(1, 1) = 0.;
67  A(1, 0) = -1.;
68  A(0, 1) = 1;
69  F.push_back(A);
70  if (do_mirror)
71  {
72  nr_flip = 8;
73  A.initIdentity();
74  A(0, 0) = -1;
75  F.push_back(A);
76  A(0, 0) = 0.;
77  A(1, 1) = 0.;
78  A(1, 0) = 1.;
79  A(0, 1) = 1;
80  F.push_back(A);
81  A(0, 0) = 1.;
82  A(1, 1) = -1.;
83  A(1, 0) = 0.;
84  A(0, 1) = 0;
85  F.push_back(A);
86  A(0, 0) = 0.;
87  A(1, 1) = 0.;
88  A(1, 0) = -1.;
89  A(0, 1) = -1;
90  F.push_back(A);
91  }
92  // Set limit_rot
93  limit_rot = (search_rot < 180.);
94 }
95 
97 {
98  //This static flag is for only randomize once
99  static bool randomized = false;
100 
101  if (!randomized)
102  {
103  std::mt19937 g(seed);
104  //-------Randomize the order of images
105  std::shuffle(img_id.begin(), img_id.end(), g);
106  randomized = true;
107  }
108 }//close function randomizeImagesOrder
109 
111 {
113  //the following will be override in the MPI implementation.
114  // nr_images_local = divide_equally(nr_images_global, size, rank, myFirstImg,
115  // myLastImg);
116 }
117 
118 // Check convergence
120 {
121 //#define DEBUG_JM
122 #ifdef DEBUG_JM
123  std::cerr<<">>>>>>>> entering checkConvergence"<<std::endl;
124 #endif
125 
126  if (iter == 0)
127  return false;
128 
129  bool converged = true;
130  double convv, avg2;
132 
133  Maux.resize(dim, dim);
134  Maux.setXmippOrigin();
135 
136  conv.clear();
137 
138  for (int refno = 0; refno < model.n_ref; refno++)
139  {
140  convv = -1;
141 
142  if (model.Iref[refno].weight() > 0.)
143  {
144  Maux = Iold[refno]() * Iold[refno]();
145  avg2 = Maux.computeAvg();
146  if (avg2 > 0.)
147  {
148  convv = 1. / avg2;
149  Maux = Iold[refno]() - model.Iref[refno]();
150  Maux = Maux * Maux;
151  convv *= Maux.computeAvg();
152  if (convv > eps)
153  converged = false;
154  }
155  }
156  conv.push_back(convv);
157  }
158 
159 #ifdef DEBUG_JM
160  std::cerr<<"<<<<<< leaving checkConvergence"<<std::endl;
161 
162 #endif
163 #undef DEBUG_JM
164 
165  return converged;
166 }//close function checkConvergence
167 
169 {
170  // Write output files
173 }
174 
175 //Standard run function of ML2D family
177 {
178  bool converged = false;
179  //CREATE_LOG(LOG_FN(fn_root));
180 
181  LOG(" starting produceSideInfo\n");
182  produceSideInfo();
183 
184  //Do some initialization work
185  LOG(" starting produceSideInfo2\n");
187 
188  //Create threads to be ready for work
189  LOG(" createThreads\n");
190  createThreads();
191 
192  LOG(" starting iterations\n");
193  // Loop over all iterations
194  for (iter = istart; !converged && iter <= Niter; iter++)
195  {
196  if (verbose)
197  std::cout << " Multi-reference refinement: iteration " << iter << " of " << Niter << std::endl;
198 
199  for (int refno = 0;refno < model.n_ref; refno++)
200  Iold[refno]() = model.Iref[refno]();
201 
202  //Perform an ML iteration
203  iteration();
204 
205  // Check convergence
206  converged = checkConvergence();
207 
208  // Do some task before ending iteration
209  endIteration();
210 
211  } // end loop iterations
212 
213  if (verbose)
214  {
215  std::cout << (converged ?
216  "--> Optimization converged!" :
217  "--> Optimization was stopped before convergence was reached!")
218  << std::endl;
219  }
220 
222  destroyThreads();
223  CLOSE_LOG();
224 }
225 
227 {
228  prog->addParamsLine(" -i <input_file> : Metadata or stack with input images ");
229 
230  String orStr = "", lb = "", rb = "";
231  if (!referenceExclusive)
232  {
233  orStr = "";
234  lb = "[";
235  rb = "]";
236  }
237  prog->addParamsLine(" [ --nref <int=1> ] : Number of references to generate automatically (recommended)");
238  prog->addParamsLine(" [ --ref <reference_file=\"\"> ] : Image, stack or metadata with initial(s) references(s)");
239 
240  prog->addParamsLine(formatString(" [ --oroot <rootname=%s> ] : Output rootname", defaultRoot.c_str()));
241  prog->addParamsLine(" [ --mirror ] : Also check mirror image of each reference ");
242 
243  if (allowFastOption)
244  {
245  prog->addParamsLine(" [ --fast ] : Use pre-centered images to pre-calculate significant orientations.");
246  prog->addParamsLine(":++ If this flag is set part of the integration over all references, rotations and translations is skipped.");
247  prog->addParamsLine(":++ The program will store all (=N_imgs*N_refs=) origin offsets that yield the maximum probability of observing");
248  prog->addParamsLine(":++ each experimental image, given each of the references. In the first iterations a complete integration over");
249  prog->addParamsLine(":++ all references, rotations and translations is performed for all images. In all subsequent iterations, for all");
250  prog->addParamsLine(":++ combinations of experimental images, references and rotations, the probability of observing the image given");
251  prog->addParamsLine(":++ the optimal origin offsets from the previous iteration is calculated. Then, if this probability is not");
252  prog->addParamsLine(":++ considered \"significant\", we assume that none of the other translations will be significant, and we skip");
253  prog->addParamsLine(":++ the integration over the translations. A combination of experimental image, reference and rotation is considered");
254  prog->addParamsLine(":++ as \"significant\" if the probability at the corresponding optimal origin offsets is larger than C times the");
255  prog->addParamsLine(":++ maximum of all these probabilities for that experimental image and reference (by default C=1e-12) This version");
256  prog->addParamsLine(":++ may run up to ten times faster than the original, complete-search approach, while practically identical results may be obtained.");
257 
258  }
259  if (allowThreads)
260  prog->addParamsLine(" [ --thr <N=1> ] : Use N parallel threads ");
261  if (allowIEM)
262  prog->addParamsLine(" [ --iem <blocks=1>] : Number of blocks to be used with IEM");
263 
264 }
265 
267 {
268  prog->addParamsLine(sectionLine);
269  prog->addParamsLine(" [ --eps <float=5e-5> ] : Stopping criterium");
270  prog->addParamsLine(formatString(" [ --iter <int=%d> ] : Maximum number of iterations to perform ", defaultNiter));
271  prog->addParamsLine(" [ --psi_step <float=5.> ] : In-plane rotation sampling interval [deg]");
272  prog->addParamsLine(" [ --noise <float=1> ] : Expected standard deviation for pixel noise ");
273  prog->addParamsLine(" [ --offset <float=3.> ] : Expected standard deviation for origin offset [pix]");
274  prog->addParamsLine(" [ --frac <docfile=\"\"> ] : Docfile with expected model fractions (default: even distr.)");
275  prog->addParamsLine(" [ -C <double=1e-12> ] : Significance criterion for fast approach ");
276  prog->addParamsLine(" [ --zero_offsets ] : Kick-start the fast algorithm from all-zero offsets ");
277  if (allowRestart)
278  prog->addParamsLine(" [ --restart <iter=1> ] : restart a run with all parameters as in the logfile ");
279  prog->addParamsLine(" [ --fix_sigma_noise ] : Do not re-estimate the standard deviation in the pixel noise ");
280  prog->addParamsLine(" [ --fix_sigma_offset ] : Do not re-estimate the standard deviation in the origin offsets ");
281  prog->addParamsLine(" [ --fix_fractions ] : Do not re-estimate the model fractions ");
282  prog->addParamsLine(" [ --student <df=6>] : Use t-distributed instead of Gaussian model for the noise ");
283  prog->addParamsLine(" : df = Degrees of freedom for the t-distribution ");
284  prog->addParamsLine(" [ --norm ] : Refined normalization parameters for each particle ");
285  prog->addParamsLine(" [ --save_memA ] : Save memory A(deprecated)");
286  prog->addParamsLine(" [ --save_memB ] : Save memory B(deprecated)");
287 
288 }
289 
291 {
292  prog->addParamsLine("==+++++ Hidden arguments ==");
293  prog->addParamsLine(" [--scratch <scratch=\"\">]");
294  prog->addParamsLine(" [--debug <int=0>]");
295  prog->addParamsLine(" [--no_sigma_trick]");
296  prog->addParamsLine(" [--trymindiff_factor <float=0.9>]");
297  prog->addParamsLine(" [--random_seed <int=-1>]");
298  prog->addParamsLine(" [--search_rot <float=999.>]");
299  prog-> addParamsLine(" [--load <N=1>]");
300 
301  //fixme: only for debug
302  prog->addParamsLine("[--no_iem] : bla bla bla");
303 }
304 
305 FileName getIterExtraPath(const FileName &fn_root, int iter, bool makePath)
306 {
307  FileName fn = formatString("%sextra/iter%03d/", fn_root.c_str(), iter);
308  if (makePath)
309  fn.makePath();
310  fn += "iter_";
311  return fn;
312 }
313 
314 
315 
318 {
319  initData();
320 }//close default constructor
321 
323 {
324  initData();
325  setNRef(n_ref);
326 }//close constructor
327 
329 {
330  n_ref = -1;
331  sumw_allrefs2 = sumw_allrefs = 0.;
332  do_student = do_norm = false;
333  do_student_sigma_trick = true;
334  sigma_noise = sigma_offset = LL = avePmax = 0.;
335  wsum_sigma_noise = wsum_sigma_offset = 0.;
336  sumfracweight = 0.;
337  dim = 0;
338 }//close function initData
339 
342 void ModelML2D::setNRef(int n_ref)
343 {
344  Image<double> Iempty;
345  Iempty().initZeros(dim,dim);
346  Iempty().setXmippOrigin();
347  this->n_ref = n_ref;
348  Iref.resize(n_ref, Iempty);
349  WsumMref.resize(n_ref, Iempty);
350  alpha_k.resize(n_ref, 0.);
351  mirror_fraction.resize(n_ref, 0.);
352  scale.resize(n_ref, 1.);
353  sumw_mirror.resize(n_ref, 0.);
354  sumwsc.resize(n_ref, 0.);
355 
356 }//close function setNRef
357 
358 
359 
361 {
362  if (n_ref != model.n_ref)
363  REPORT_ERROR(ERR_VALUE_INCORRECT, "Can not add models with different 'n_ref'");
364 
365 #define COMBINE(var) var += sign * model.var
366  COMBINE(wsum_sigma_offset);
367  COMBINE(wsum_sigma_noise);
368  COMBINE(sumw_allrefs);
369  COMBINE(sumw_allrefs2);
370  COMBINE(sumfracweight);
371  COMBINE(LL);
372 
374  double sumweight = 0., w1 = 0., w2 = 0.;
375 
376  for (int refno = 0; refno < n_ref; refno++)
377  {
378  w1 = WsumMref[refno].weight();
379  w2 = sign * model.WsumMref[refno].weight();
380  tmp = model.WsumMref[refno]();
381  tmp *= sign;
382  WsumMref[refno]() += tmp;
383  sumweight = w1 + w2;
384 
385  if (sumweight > 0.)
386  {
387  Iref[refno].setWeight(sumweight);
388  WsumMref[refno].setWeight(sumweight);
389  COMBINE(sumw_mirror[refno]);
390  COMBINE(sumwsc[refno]);
391  }
392  else
393  {
394  Iref[refno]().initZeros();
395  WsumMref[refno]().initZeros();
396  Iref[refno].setWeight(0);
397  WsumMref[refno].setWeight(0.);
398  sumw_mirror[refno] = 0.;
399  sumwsc[refno] = 0.;
400  }
401  }
402 }//close function combineModel
403 
405 {
406  combineModel(model, 1);
407 }//close function addModel
408 
410 {
411  combineModel(model, -1);
412 }//close function substractModel
413 
415 {
416  if (sumw_allrefs < 0)
417  REPORT_ERROR(ERR_VALUE_INCORRECT, "updateFractions: sumw_allrefs should be greater than 0 ");
418 
419  //update sigma_noise
420  double sum = (do_student && do_student_sigma_trick) ? sumw_allrefs2
421  : sumw_allrefs;
422  double sigma_noise2 = wsum_sigma_noise / (sum * dim * dim);
423 
424  if (sigma_noise2 < 0.)
425  REPORT_ERROR(ERR_VALUE_INCORRECT, "sqrt of negative 'sigma_noise2'");
426  sigma_noise = sqrt(sigma_noise2);
427 
428  //update sigma_offset
429  if (wsum_sigma_offset < 0.)
430  REPORT_ERROR(ERR_VALUE_INCORRECT, "sqrt of negative 'wsum_sigma_offset'");
431  sigma_offset = sqrt(wsum_sigma_offset / (2. * sumw_allrefs));
432 
433  //update avePmax
434  avePmax = sumfracweight / sumw_allrefs;
435 
436  double weight, inv_weight;
437 
438 // std::cerr << "DEBUG_JM: sumw_allrefs: " << sumw_allrefs << std::endl;
439 
440  for (int refno = 0; refno < n_ref; ++refno)
441  {
442  weight = WsumMref[refno].weight();
443 
444 // std::cerr << " DEBUG_JM: refno: " << refno << std::endl;
445 
446  if (weight > 0.)
447  {
448  //update weights
449  Iref[refno].setWeight(weight);
450  inv_weight = 1 / weight; //just to speed-up
451  Iref[refno]() = WsumMref[refno]();
452  Iref[refno]() *= inv_weight;
453  //update fractions
454  alpha_k[refno] = weight / sumw_allrefs;
455  mirror_fraction[refno] = sumw_mirror[refno] * inv_weight;
456  scale[refno] = sumwsc[refno] * inv_weight;
457 // std::cerr << " DEBUG_JM: weight: " << weight << std::endl;
458 // std::cerr << " DEBUG_JM: sumw_mirror[refno]: " << sumw_mirror[refno] << std::endl;
459  }
460  else
461  {
462 // std::cerr << " DEBUG_JM: all zeros... " << std::endl;
463  //zero weights
464  Iref[refno].setWeight(0.);
465  Iref[refno]().initZeros(WsumMref[refno]());
466  alpha_k[refno] = 0.;
467  mirror_fraction[refno] = 0.;
468  scale[refno] = 0.;
469  }
470  }
471 }
472 
473 #define pp(s, x) s << std::setw(10) << x
474 
475 void ModelML2D::print(int tabs) const
476 {
477  String stabs = "";
478  for (int t = 0; t < tabs; ++t)
479  stabs += " ";
480 
481  std::cerr << "======================= Block ==================== " << std::endl;
482 
483  std::cerr << "sumw_allrefs: " << sumw_allrefs << std::endl;
484  std::cerr << "wsum_sigma_offset: " << wsum_sigma_offset << std::endl;
485  std::cerr << "wsum_sigma_noise: " << wsum_sigma_noise << std::endl;
486  std::cerr << "sigma_offset: " << sigma_offset << std::endl;
487  std::cerr << "sigma_noise: " << sigma_noise << std::endl;
488  std::cerr << "LL: " << LL << std::endl;
489 
490  std::stringstream ss1, ss2, ss3, ss4, ss5, ss6, ss7;
491  pp(ss1, "refno:");
492  pp(ss3, "sumw_mirror:");
493  pp(ss4, "alpha_k:");
494  pp(ss5, "mirror_fraction");
495  pp(ss6, "WsumMref.weight");
496  pp(ss7, "Iref.weight");
497 
498  for (int refno = 0; refno < n_ref; refno++)
499  {
500  pp(ss1, refno);
501  pp(ss3, sumw_mirror[refno]);
502  pp(ss4, alpha_k[refno]);
503  pp(ss5, mirror_fraction[refno]);
504  pp(ss6, WsumMref[refno].weight());
505  pp(ss7, Iref[refno].weight());
506  }
507  std::cerr << ss1.str() << std::endl<< ss3.str() << std::endl
508  << ss4.str() << std::endl<< ss5.str() << std::endl << ss6.str() << std::endl << ss7.str()<< std::endl;
509 
510 }//close function print
511 
512 
513 
514 
515 
int defaultNiter
Definition: ml2d.h:241
size_t nr_psi
Definition: ml2d.h:154
std::vector< size_t > img_id
Definition: ml2d.h:232
ModelML2D()
Definition: ml2d.cpp:317
int refs_per_class
Definition: ml2d.h:217
Definition: ml2d.h:76
void resize(size_t Ndim, size_t Zdim, size_t Ydim, size_t Xdim, bool copy=true)
size_t nr_flip
Definition: ml2d.h:156
#define REPORT_ERROR(nerr, ErrormMsg)
Definition: xmipp_error.h:211
double sign
doublereal * g
virtual void produceSideInfo()=0
Try to merge produceSideInfo1 and 2.
#define pp(s, x)
Definition: ml2d.cpp:473
void sqrt(Image< double > &op)
ModelML2D model
Definition: ml2d.h:224
virtual void destroyThreads()
Exit threads and free memory.
Definition: ml2d.h:294
virtual void createThreads()
Create working threads.
Definition: ml2d.h:289
bool referenceExclusive
Definition: ml2d.h:239
size_t myLastImg
Definition: ml2d.h:168
void substractModel(const ModelML2D &model)
Definition: ml2d.cpp:409
int n_ref
Definition: ml2d.h:83
double psi_step
Definition: ml2d.h:158
bool allowRestart
Definition: ml2d.h:239
virtual void iteration()=0
void setNRef(int n_ref)
Definition: ml2d.cpp:342
ML2DBaseProgram()
Definition: ml2d.cpp:30
FileName fn_root
Definition: ml2d.h:132
double psi_max
Definition: ml2d.h:160
virtual void defineAdditionalParams(XmippProgram *prog, const char *sectionLine)
Definition: ml2d.cpp:266
void update()
Definition: ml2d.cpp:414
bool do_norm
Re-normalize internally.
Definition: ml2d.h:207
void print(int tabs=0) const
Just for debugging now.
Definition: ml2d.cpp:475
String defaultRoot
Definition: ml2d.h:240
bool do_ML3D
Definition: ml2d.h:196
ProgTransformDimRed * prog
virtual void run()
Main function of the program.
Definition: ml2d.cpp:176
size_t nr_images_local
Definition: ml2d.h:166
void combineModel(const ModelML2D &model, int sign)
Definition: ml2d.cpp:360
double search_rot
Definition: ml2d.h:190
bool limit_rot
Definition: ml2d.h:188
size_t myFirstImg
Definition: ml2d.h:168
#define CEIL(x)
Definition: xmipp_macros.h:225
void initSamplingStuff()
Definition: ml2d.cpp:44
virtual void defineBasicParams(XmippProgram *prog)
Definition: ml2d.cpp:226
std::vector< Matrix2D< double > > F
Definition: ml2d.h:174
FileName getIterExtraPath(const FileName &fn_root, int iter, bool makePath)
Definition: ml2d.cpp:305
#define COMBINE(var)
MultidimArray< double > docfiledata
Definition: ml2d.h:234
int istart
Definition: ml2d.h:145
int makePath(mode_t mode=0755) const
size_t dim
Definition: ml2d.h:151
int factor_nref
Definition: ml2d.h:215
int verbose
Verbosity level.
virtual void defineHiddenParams(XmippProgram *prog)
Definition: ml2d.cpp:290
virtual void addPartialDocfileData(const MultidimArray< double > &data, size_t first, size_t last)=0
Add docfiledata to docfile.
#define CLOSE_LOG()
void initData()
Definition: ml2d.cpp:328
bool allowFastOption
Definition: ml2d.h:239
#define LOG(msg)
virtual void setNumberOfLocalImages()
Set the number of images, this function is useful only for MPI.
Definition: ml2d.cpp:110
bool allowIEM
Definition: ml2d.h:239
std::vector< Image< double > > Iref
Definition: ml2d.h:85
std::string String
Definition: xmipp_strings.h:34
String formatString(const char *format,...)
Definition: ml2d.h:79
virtual bool checkConvergence()
Definition: ml2d.cpp:119
String outRefsMd
Definition: ml2d.h:250
virtual void randomizeImagesOrder()
Randomize initial images order, only once.
Definition: ml2d.cpp:96
bool do_mirror
Definition: ml2d.h:137
size_t nr_images_global
Definition: ml2d.h:164
virtual void produceSideInfo2()=0
Try to merge produceSideInfo1 and 2.
double computeAvg() const
std::vector< Image< double > > WsumMref
Definition: ml2d.h:85
Definition: ml2d.h:76
size_t nr_nomirror_flips
Definition: ml2d.h:162
Incorrect value received.
Definition: xmipp_error.h:195
std::vector< Image< double > > Iold
Definition: ml2d.h:176
std::vector< double > conv
Definition: ml2d.h:219
size_t blocks
Definition: ml2d.h:227
bool allowThreads
Definition: ml2d.h:239
virtual void endIteration()
Do some task at the end of iteration.
Definition: ml2d.cpp:168
void addParamsLine(const String &line)
void addModel(const ModelML2D &model)
Definition: ml2d.cpp:404
double eps
Definition: ml2d.h:170
void initIdentity()
Definition: matrix2d.h:673
virtual void writeOutputFiles(const ModelML2D &model, OutputType outputType=OUT_FINAL)=0
Write output files.