LevenbergMarquardt.h
1//-*-C++-*-
2/***************************************************************************
3 *
4 * Copyright (C) 2004 by Willem van Straten
5 * Licensed under the Academic Free License version 2.1
6 *
7 ***************************************************************************/
8
9// psrchive/More/MEAL/MEAL/LevenbergMarquardt.h
10
11#ifndef __Levenberg_Marquardt_h
12#define __Levenberg_Marquardt_h
13
14#include "MEAL/GaussJordan.h"
15#include "MEAL/Axis.h"
16#include "Estimate.h"
17#include "Error.h"
18#include "true_math.h"
19
20#include <iostream>
21#include <cmath>
22
23namespace MEAL
24{
25 class RestorePolicy;
26
28
38 unsigned get_nparam () const;
39
41 double get_param (unsigned iparam) const;
42
44 void set_param (unsigned iparam, ) const;
45
47 bool get_infit (unsigned index) const;
48
50 Yt evaluate (std::vector<Gt>* gradient);
51
52 };
53 </pre>
54
55 The type of the gradient, Gt, is explicity specified in the
56 declaration of this template class. The types of At and Yt are
57 implicitly specified by the template instantiation of the methods
58 of this class. If Yt or Gt is not of type float or double, there
59 must also be defined:
60
61 <pre>
63 const Yt operator - (const Yt &, const Yt &);
64
66 const Gt operator * (const Yt &, const Gt &);
67
68 <\pre>
69
70 The LevenbergMarquardt class is used in three stages:
71 <UL>
72 <LI> call to ::init() with data and model
73 <LI> repeated calls to ::iter() with data and model, comparing the chisq
74 returned in order to determine convergence (or not) of fit
75 <LI>call to ::result() to get curvature and covariance matrices
76 </UL>
77 */
78 template <class Grad>
79 class LevenbergMarquardt
80 {
81
82 public:
83 static unsigned verbose;
84
85 LevenbergMarquardt ()
86 {
87 lamda_increase_factor = 10.0;
88 lamda_decrease_factor = 0.1;
89 singular_threshold = 1e-8;
90 restore_policy = NULL;
91 }
92
94
99 template <class At, class Et, class Mt>
100 float init (const std::vector< At >& x,
101 const std::vector< Et >& y,
102 Mt& model);
103
105 template <class At, class Et, class Mt>
106 float iter (const std::vector< At >& x,
107 const std::vector< Et >& y,
108 Mt& model);
109
111 template <class Mt>
112 void result (Mt& model,
113 std::vector<std::vector<double> >& covariance = null_arg,
114 std::vector<std::vector<double> >& curvature = null_arg);
115
117 double get_log_det_curvature() const { return log_det_alpha; }
118
120 unsigned get_nparam_infit() const { return nparam_infit; }
121
123 float lamda;
124
125 float lamda_increase_factor;
126 float lamda_decrease_factor;
127
129
131 float singular_threshold;
132
133 RestorePolicy* restore_policy;
134
135 protected:
136
138 template <class Mt> void solve_delta (const Mt& model);
139
141 template <class At, class Et, class Mt>
142 float calculate_chisq (const std::vector< At >& x,
143 const std::vector< Et >& y,
144 Mt& model);
145
146 private:
147
149 std::vector<Grad> gradient;
150
152 std::vector<double> beta;
153
155 std::vector<std::vector<double> > alpha;
156 double log_det_alpha = 0.0;
157
159 std::vector<std::vector<double> > delta;
160
162 std::vector<std::string> names;
163 std::vector<const char*> name_ptrs;
164
166 float best_chisq = 0;
167
169 std::vector<std::vector<double> > best_alpha;
171 std::vector<double> best_beta;
172
174 unsigned nparam_infit = 0;
175
177 std::vector<double> backup;
178
179 static std::vector<std::vector<double> > null_arg;
180 };
181
182 template<class At>
183 class AbscissaTraits
184 {
185 public:
186 template<class Mt>
187 static void apply (Mt& model, const At& abscissa)
188 { abscissa.apply(); }
189 };
190
191 template<>
192 class AbscissaTraits<double>
193 {
194 public:
195 template<class Mt>
196 static void apply (Mt& model, double abscissa)
197 { model.set_abscissa(abscissa); }
198 };
199
200 class RestorePolicy
201 {
202 public:
204 virtual void store () = 0;
206 virtual void restore () = 0;
207 };
208
210 template<class Et>
211 class WeightingScheme
212 {
213 public:
214
215 typedef typename Et::val_type val_type;
216 typedef typename Et::var_type var_type;
217
219 WeightingScheme (const Et& estimate)
220 {
221 set_variance (estimate.get_variance());
222 }
223
225 void set_variance (const var_type& variance)
226 {
227 inverse_variance = 1.0 / variance;
228 }
229
231 val_type difference (const Et& estimate, const val_type& model)
232 {
233 return estimate.get_value() - model;
234 }
235
237 val_type norm (const val_type& x) const
238 {
239 return x*x;
240 }
241
242 val_type get_weighted_conjugate (const val_type& data) const
243 {
244 return data * inverse_variance;
245 }
246
247 float get_weighted_norm (const val_type& data) const
248 {
249 return norm(data) * inverse_variance;
250 }
251
252 var_type inverse_variance;
253
254 };
255
256
257
259 template<class Et>
260 class WeightingScheme< std::complex<Et> >
261 {
262
263 public:
264
265 typedef std::complex<Et> type;
266 typedef std::complex<typename Et::val_type> val_type;
267 typedef typename Et::var_type var_type;
268
270 WeightingScheme (const type& estimate)
271 {
272 set_variance (estimate);
273 }
274
276 void set_variance (const type& estimate)
277 {
278 inv_var_real = 1.0 / estimate.real().get_variance();
279 inv_var_imag = 1.0 / estimate.imag().get_variance();
280 }
281
283 val_type difference (const type& estimate, const val_type& model)
284 {
285 val_type val (estimate.real().get_value(), estimate.imag().get_value());
286 return val - model;
287 }
288
290 val_type norm (const val_type& x) const
291 {
292 return std::norm(x);
293 }
294
295 val_type get_weighted_conjugate (const val_type& data) const
296 {
297 return val_type (data.real()*inv_var_real, -data.imag()*inv_var_imag);
298 }
299
300 float get_weighted_norm (const val_type& data) const
301 {
302 return data.real()*data.real()*inv_var_real + data.imag()*data.imag()*inv_var_imag;
303 }
304
305 var_type inv_var_real;
306 var_type inv_var_imag;
307 };
308
309
311 template <class Mt, class At, class Et, class Grad>
312 float lmcoff (// input
313 Mt& model,
314 const At& abscissa,
315 const Et& data,
316 // storage
317 std::vector<Grad>& gradient,
318 // output
319 std::vector<std::vector<double> >& alpha,
320 std::vector<double>& beta);
321
323
324 template <class Mt, class Yt, class Wt, class Grad>
325 float lmcoff1 (// input
326 Mt& model,
327 const Yt& delta_data,
328 const Wt& weighting_scheme,
329 const std::vector<Grad>& gradient,
330 // output
331 std::vector<std::vector<double> >& alpha,
332 std::vector<double>& beta);
333
334 template<class Mt>
335 std::string get_name (const Mt& model, unsigned iparam);
336
337}
338
339template <class Grad>
340std::vector<std::vector<double> > MEAL::LevenbergMarquardt<Grad>::null_arg;
341
342template <class Grad>
343unsigned MEAL::LevenbergMarquardt<Grad>::verbose = 0;
344
345template <class Grad>
346template <class At, class Et, class Mt>
347float MEAL::LevenbergMarquardt<Grad>::init
348(const std::vector< At >& x,
349 const std::vector< Et >& y,
350 Mt& model)
351{
352 if (verbose > 2)
353 std::cerr << "MEAL::LevenbergMarquardt<Grad>::init" << std::endl;
354
355 // size all of the working space arrays
356 alpha.resize (model.get_nparam());
357 beta.resize (model.get_nparam());
358 delta.resize (model.get_nparam());
359 backup.resize (model.get_nparam());
360 names.resize (model.get_nparam());
361 name_ptrs.resize (model.get_nparam());
362
363 for (unsigned j=0; j<model.get_nparam(); j++)
364 {
365 alpha[j].resize (model.get_nparam());
366 delta[j].resize (1);
367 }
368
369 if (verbose > 2)
370 std::cerr << "MEAL::LevenbergMarquardt<Grad>::init calculate chisq" << std::endl;
371
372 best_chisq = calculate_chisq (x, y, model);
373 best_alpha = alpha;
374 best_beta = beta;
375 lamda = 0.001;
376
377 if (verbose > 0)
378 std::cerr << "MEAL::LevenbergMarquardt<Grad>::init chisq=" << best_chisq << std::endl;
379
380 return best_chisq;
381}
382
383template<class T>
384void verify_orthogonal (const std::vector<std::vector<double > >& alpha, const T& model)
385{
386 unsigned nrow = alpha.size();
387
388 if (!nrow)
389 return;
390
391 unsigned nparam = model.get_nparam ();
392
393 unsigned nfree = 0;
394 for (unsigned iparam=0; iparam < nparam; iparam++)
395 if (model.get_infit(iparam))
396 nfree ++;
397
398 /*
399 Convert row numbers to parameter names
400 */
401 std::vector<std::string> names (nfree);
402 std::vector<unsigned> indeces (nfree);
403
404 unsigned kparam = 0;
405 for (unsigned krow=0; krow<nfree; krow++)
406 {
407 while (!model.get_infit(kparam))
408 kparam ++;
409
410 names[krow] = model.get_param_name(kparam);
411 indeces[krow] = kparam;
412
413 kparam ++;
414 }
415
416 std::vector<double> row_mod (nfree, 0.0);
417
418 /*
419 calculate the norm of each row vector
420 */
421
422 for (unsigned irow=0; irow<nfree; irow++)
423 {
424 double norm = 0.0;
425 for (unsigned jcol=0; jcol<nfree; jcol++)
426 norm += alpha[irow][jcol] * alpha[irow][jcol];
427
428 row_mod[irow] = sqrt(norm);
429
430 if (row_mod[irow] == 0)
431 std::cerr << irow << "=" << names[irow] << " gradient = 0" << std::endl;
432 }
433
434 for (unsigned krow=0; krow<nfree; krow++)
435 {
436 if (row_mod[krow] == 0)
437 continue;
438
439 for (unsigned irow=krow+1; irow<nfree; irow++)
440 {
441 if (row_mod[irow] == 0)
442 {
443 continue;
444 }
445
446 double degen = 0.0;
447 for (unsigned jcol=0; jcol<nfree; jcol++)
448 {
449 degen += alpha[krow][jcol] * alpha[irow][jcol];
450 }
451
452 degen /= row_mod[krow] * row_mod[irow];
453
454 if (degen > 0.8)
455 {
456 double ival = model.get_param(indeces[irow]);
457 double kval = model.get_param(indeces[krow]);
458
459 std::cerr << "degen(" << names[krow] << "," << names[irow] << ") = "
460 << degen << std::endl
461 << "\t" << names[krow] << " = " << kval << std::endl
462 << "\t" << names[irow] << " = " << ival << std::endl;
463 }
464
465 if (!true_math::finite(degen))
466 {
467 std::cerr << "NaN or Inf in curvature matrix" << std::endl;
468 return;
469 }
470 }
471 }
472}
473
474template<typename Mt>
475std::string MEAL::get_name (const Mt& model, unsigned iparam)
476{
477 unsigned ifree = 0;
478 for (unsigned i=0; i < model.get_nparam(); i++)
479 {
480 if (model.get_infit(i))
481 {
482 if (ifree == iparam)
483 return model.get_param_name(i);
484 ifree ++;
485 }
486 }
487 return "unknown";
488}
489
490// /////////////////////////////////////////////////////////////////////////
491// MEAL::LevenbergMarquardt<Grad>::solve_delta
492// /////////////////////////////////////////////////////////////////////////
493
502template <class Grad>
503template <class Mt>
504void MEAL::LevenbergMarquardt<Grad>::solve_delta (const Mt& model)
505{
506 if (verbose > 2)
507 std::cerr << "MEAL::LevenbergMarquardt<Grad>::solve_delta" << std::endl;
508
509 if (alpha.size() != model.get_nparam())
510 {
511 throw Error (InvalidState,
512 "MEAL::LevenbergMarquardt<Grad>::solve_delta",
513 "alpha.size=%d != model.nparam=%d",
514 alpha.size(), model.get_nparam());
515 }
516
517 if (verbose > 0)
518 {
519 std::cerr << "MEAL::LevenbergMarquardt<Grad>::solve_delta lamda="
520 << lamda << " nparam=" << model.get_nparam() << std::endl;
521 }
522
523 unsigned iinfit = 0;
524 for (unsigned ifit=0; ifit<model.get_nparam(); ifit++)
525 {
526 if (verbose > 0)
527 std::cerr << "MEAL::LevenbergMarquardt<Grad>::solve_delta i=" << ifit
528 << " " << model.get_param_name(ifit);
529
530 if (model.get_infit(ifit))
531 {
532 if (verbose > 0)
533 std::cerr << " in fit" << std::endl;
534
535 unsigned jinfit = 0;
536 for (unsigned jfit=0; jfit<model.get_nparam(); jfit++)
537 {
538 if (model.get_infit(jfit))
539 {
540 alpha[iinfit][jinfit]=best_alpha[ifit][jfit];
541 jinfit ++;
542 }
543 }
544
545 alpha[iinfit][iinfit] *= (1.0 + lamda);
546 delta[iinfit][0]=best_beta[ifit];
547 names[iinfit] = model.get_param_name(ifit);
548 name_ptrs[iinfit] = names[iinfit].c_str();
549
550 iinfit ++;
551 }
552 else if (verbose > 0)
553 std::cerr << " fixed" << std::endl;
554 }
555
556 if (iinfit == 0)
557 throw Error (InvalidState, "MEAL::LevenbergMarquardt<Grad>::solve_delta", "no parameters in fit");
558
559 nparam_infit = iinfit;
560
561 if (verbose > 2)
562 std::cerr << "MEAL::LevenbergMarquardt<Grad>::solve_delta for " << iinfit << " parameters" << std::endl;
563
565 std::vector<std::vector<double> > temp_copy (alpha);
566
567 try
568 {
569 // invert Equation 15.5.14
570 log_det_alpha = MEAL::GaussJordan (alpha, delta, iinfit, singular_threshold, &name_ptrs);
571 }
572 catch (Error& error)
573 {
574 if (verbose > 0)
575 verify_orthogonal (temp_copy, model);
576 throw error += "MEAL::LevenbergMarquardt<Grad>::solve_delta";
577 }
578
579 if (verbose > 2)
580 std::cerr << "MEAL::LevenbergMarquardt<Grad>::solve_delta exit" << std::endl;
581}
582
583// /////////////////////////////////////////////////////////////////////////
584// MEAL::LevenbergMarquardt<Grad>::iter
585// /////////////////////////////////////////////////////////////////////////
586
604template <class Grad>
605template <class At, class Et, class Mt>
606float MEAL::LevenbergMarquardt<Grad>::iter
607( const std::vector< At >& x,
608 const std::vector< Et >& y,
609 Mt& model )
610{
611 if (verbose > 2)
612 std::cerr << "MEAL::LevenbergMarquardt<Grad>::iter" << std::endl;
613
614 solve_delta (model);
615
616 // After call to solve_delta, delta contains required change in model
617 // parameters. Update the model.
618
619 if (verbose > 2)
620 std::cerr << "MEAL::LevenbergMarquardt<Grad>::iter update model" << std::endl;
621
622 unsigned iinfit = 0;
623 for (unsigned ifit=0; ifit<model.get_nparam(); ifit++)
624 {
625 double change = 0.0;
626
627 if (model.get_infit(ifit))
628 {
629 change = delta[iinfit][0];
630 iinfit ++;
631 }
632
633 backup[ifit] = model.get_param (ifit);
634
635 if (verbose > 2)
636 std::cerr << " delta[" << ifit << "]=" << change << std::endl;
637
638 model.set_param (ifit, backup[ifit] + change);
639 }
640
641 if (verbose > 2)
642 std::cerr << "MEAL::LevenbergMarquardt<Grad>::iter calculate new chisq" << std::endl;
643
644 float new_chisq = calculate_chisq (x, y, model);
645
646 if (new_chisq < best_chisq)
647 {
648 lamda *= lamda_decrease_factor;
649
650 if (verbose)
651 std::cerr << "MEAL::LevenbergMarquardt<Grad>::iter new chisq="
652 << new_chisq << "\n better fit; lamda=" << lamda << std::endl;
653
654 if (restore_policy)
655 restore_policy->store ();
656
657 best_chisq = new_chisq;
658 best_alpha = alpha;
659 best_beta = beta;
660 }
661 else
662 {
663 lamda *= lamda_increase_factor;
664
665 if (verbose)
666 std::cerr << "MEAL::LevenbergMarquardt<Grad>::iter new chisq="
667 << new_chisq << "\n worse fit; lamda=" << lamda << std::endl;
668
669 if (restore_policy)
670 restore_policy->restore ();
671
672 // restore the old model
673 for (unsigned iparm=0; iparm<model.get_nparam(); iparm++)
674 model.set_param (iparm, backup[iparm]);
675 }
676
677 return new_chisq;
678}
679
680// /////////////////////////////////////////////////////////////////////////
681// MEAL::LevenbergMarquardt<Grad>::result
682// /////////////////////////////////////////////////////////////////////////
683
692template <class Grad>
693template <class Mt>
694void MEAL::LevenbergMarquardt<Grad>::result
695( Mt& model,
696 std::vector<std::vector<double> >& covar,
697 std::vector<std::vector<double> >& curve )
698{
699 if (verbose > 2)
700 std::cerr << "MEAL::LevenbergMarquardt<Grad>::result" << std::endl;
701
702 if (&curve != &null_arg)
703 curve = best_alpha;
704
705 lamda = 0.0;
706 solve_delta (model);
707
708 if (&covar == &null_arg)
709 return;
710
711 covar.resize (model.get_nparam());
712
713 unsigned iindim = 0;
714 for (unsigned idim=0; idim < model.get_nparam(); idim++)
715 {
716 covar[idim].resize (model.get_nparam());
717
718 if (!model.get_infit(idim))
719 {
720 for (unsigned jdim=0; jdim < model.get_nparam(); jdim++)
721 covar[idim][jdim] = 0;
722 }
723 else
724 {
725 unsigned jindim = 0;
726 for (unsigned jdim=0; jdim < model.get_nparam(); jdim++)
727 {
728 if (model.get_infit(jdim))
729 {
730 covar[idim][jdim] = alpha [iindim][jindim];
731 jindim ++;
732 }
733 else
734 covar[idim][jdim] = 0;
735 }
736 iindim ++;
737 }
738 }
739}
740
741// /////////////////////////////////////////////////////////////////////////
742// MEAL::LevenbergMarquardt<Grad>::chisq
743// /////////////////////////////////////////////////////////////////////////
744
756template <class Grad>
757template <class At, class Et, class Mt>
758float MEAL::LevenbergMarquardt<Grad>::calculate_chisq
759(const std::vector< At >& x,
760 const std::vector< Et >& y,
761 Mt& model)
762{
763 if (verbose > 2)
764 std::cerr << "MEAL::LevenbergMarquardt<Grad>::chisq nparam="
765 << model.get_nparam() << std::endl;
766
767 if (alpha.size() != model.get_nparam())
768 throw Error (InvalidState, "MEAL::LevenbergMarquardt<Grad>::chisq",
769 "alpha.size=%d != model.nparam=%d",
770 alpha.size(), model.get_nparam());
771
772 if (y.size() < x.size())
773 throw Error (InvalidParam, "MEAL::LevenbergMarquardt<Grad>::chisq",
774 "y.size=%d < x.size=%d", y.size(), x.size());
775
776 // initialize sums
777 double Chisq = 0.0;
778 for (unsigned j=0; j<alpha.size(); j++)
779 {
780 for (unsigned k=0; k<=j; k++)
781 alpha[j][k] = 0.0;
782 beta[j] = 0.0;
783 }
784
785 for (unsigned ipt=0; ipt < x.size(); ipt++)
786 {
787 if (verbose > 2)
788 std::cerr << "MEAL::LevenbergMarquardt<Grad>::chisq lmcoff[" << ipt << "/" << x.size() << "]" << std::endl;
789
790 Chisq += lmcoff (model, x[ipt], y[ipt], gradient, alpha, beta);
791 }
792
793 // populate the symmetric half of the curvature matrix
794 for (unsigned ifit=1; ifit<model.get_nparam(); ifit++)
795 for (unsigned jfit=0; jfit<ifit; jfit++)
796 alpha[jfit][ifit]=alpha[ifit][jfit];
797
798 return Chisq;
799}
800
801template <class Mt, class At, class Et, class Grad>
802float MEAL::lmcoff (
803 // input
804 Mt& model,
805 const At& abscissa,
806 const Et& data,
807 // storage
808 std::vector<Grad>& gradient,
809 // output
810 std::vector<std::vector<double> >& alpha,
811 std::vector<double>& beta
812 )
813try
814{
815 if (LevenbergMarquardt<Grad>::verbose > 2)
816 std::cerr << "MEAL::lmcoff data=" << data << std::endl;
817
818 AbscissaTraits<At>::apply (model, abscissa);
819
820 if (LevenbergMarquardt<Grad>::verbose > 2)
821 std::cerr << "MEAL::lmcoff abscissa applied" << std::endl;
822
823 WeightingScheme<Et> weight (data);
824
825 float result = lmcoff1 (model,
826 weight.difference (data, model.evaluate (&gradient)),
827 weight, gradient, alpha, beta);
828
829 if (LevenbergMarquardt<Grad>::verbose > 2)
830 std::cerr << "MEAL::lmcoff lmcoff1 computed" << std::endl;
831
832 return result;
833}
834catch (Error& error)
835{
836 error << "\n\t" "data=" << data << " model=" << model.evaluate ();
837 throw error += "MEAL::lmcoff";
838}
839
840
841template <class Mt, class Yt, class Wt, class Grad>
842float MEAL::lmcoff1 (
843 // input
844 Mt& model,
845 const Yt& delta_y,
846 const Wt& weight,
847 const std::vector<Grad>& gradient,
848 // output
849 std::vector<std::vector<double> >& alpha,
850 std::vector<double>& beta
851 )
852try
853{
855 ElementTraits<Grad> traits;
856
857 if (LevenbergMarquardt<Grad>::verbose > 2)
858 std::cerr << "MEAL::lmcoff1 delta_y=" << delta_y << std::endl;
859
860 Yt w_delta_y = weight.get_weighted_conjugate (delta_y);
861
862 for (unsigned ifit=0; ifit < model.get_nparam(); ifit++)
863 {
864 if (model.get_infit(ifit))
865 {
866 double term = traits.to_real (w_delta_y * gradient[ifit]);
867 if (!true_math::finite(term))
868 throw Error (InvalidState, "MEAL::lmcoff1"
869 "non-finite contribution to beta");
870
871 // Equation 15.5.6 (with 15.5.8)
872 beta[ifit] += term;
873
874 if (LevenbergMarquardt<Grad>::verbose > 2)
875 std::cerr << "MEAL::lmcoff1 compute weighted conjugate of gradient"
876 "[" << ifit << "]" << std::endl;
877
878 Grad w_gradient = weight.get_weighted_conjugate (gradient[ifit]);
879
880 if (LevenbergMarquardt<Grad>::verbose > 2)
881 std::cerr << "MEAL::lmcoff1 add to curvature matrix" << std::endl;
882
883 // Equation 15.5.11 of NR
884 for (unsigned jfit=0; jfit <= ifit; jfit++)
885 {
886 if (model.get_infit(jfit))
887 {
888 double term = traits.to_real (w_gradient * gradient[jfit]);
889 if (!true_math::finite(term))
890 throw Error (InvalidState, "MEAL::lmcoff1", "non-finite contribution to alpha");
891
892 alpha[ifit][jfit] += term;
893 }
894 }
895 }
896 }
897
898 // Equation 15.5.5
899 float chisq = weight.get_weighted_norm (delta_y);
900
901 if (LevenbergMarquardt<Grad>::verbose > 1 || !true_math::finite(chisq))
902 std::cerr << "MEAL::lmcoff1 chisq=" << chisq << std::endl;
903
904 return chisq;
905}
906catch (Error& error)
907{
908 error << "\n\t" "delta_y=" << delta_y;
909 throw error += "MEAL::lmcoff1";
910}
911
912#endif
Namespace in which all modeling and calibration related code is declared.
Definition ExampleComplex2.h:16
T GaussJordan(std::vector< std::vector< T > > &a, std::vector< std::vector< U > > &b, int nrow=-1, double singular_threshold=0.0, std::vector< const char * > *names=0)
Definition GaussJordan.h:44
const ScalarMath sqrt(const ScalarMath &x)
Return a ScalarMath instance representing x^.5.
Definition ScalarMath.C:151
bool verbose

Generated using doxygen 1.14.0