SourceXtractorPlusPlus  0.19
SourceXtractor++, the next generation SExtractor
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
LevmarEngine.cpp
Go to the documentation of this file.
1 
23 #include <array>
24 #include <cmath>
25 #include <mutex>
26 
27 #include <levmar.h>
29 #include <ElementsKernel/Logging.h>
32 
33 #ifndef LEVMAR_WORKAREA_MAX_SIZE
34 #define LEVMAR_WORKAREA_MAX_SIZE size_t(2ul<<30) // 2 GiB
35 #endif
36 
37 namespace {
38 
39 __attribute__((unused))
40 void printLevmarInfo(std::array<double, 10> info) {
41  std::cerr << "\nMinimization info:\n";
42  std::cerr << " ||e||_2 at initial p: " << info[0] << '\n';
43  std::cerr << " ||e||_2: " << info[1] << '\n';
44  std::cerr << " ||J^T e||_inf: " << info[2] << '\n';
45  std::cerr << " ||Dp||_2: " << info[3] << '\n';
46  std::cerr << " mu/max[J^T J]_ii: " << info[4] << '\n';
47  std::cerr << " # iterations: " << info[5] << '\n';
48  switch ((int) info[6]) {
49  case 1:
50  std::cerr << " stopped by small gradient J^T e\n";
51  break;
52  case 2:
53  std::cerr << " stopped by small Dp\n";
54  break;
55  case 3:
56  std::cerr << " stopped by itmax\n";
57  break;
58  case 4:
59  std::cerr << " singular matrix. Restart from current p with increased mu\n";
60  break;
61  case 5:
62  std::cerr << " no further error reduction is possible. Restart with increased mu\n";
63  break;
64  case 6:
65  std::cerr << " stopped by small ||e||_2\n";
66  break;
67  case 7:
68  std::cerr << " stopped by invalid (i.e. NaN or Inf) func values; a user error\n";
69  break;
70  }
71  std::cerr << " # function evaluations: " << info[7] << '\n';
72  std::cerr << " # Jacobian evaluations: " << info[8] << '\n';
73  std::cerr << " # linear systems solved: " << info[9] << "\n\n";
74 }
75 
76 }
77 
78 namespace ModelFitting {
79 
81 
82 static std::shared_ptr<LeastSquareEngine> createLevmarEngine(unsigned max_iterations) {
83  return std::make_shared<LevmarEngine>(max_iterations);
84 }
85 
87 
89  if (res == -1) {
91  }
92  switch (static_cast<int>(info[6])) {
93  case 7:
94  case 4:
96  case 3:
98  default:
100  }
101 }
102 
103 LevmarEngine::LevmarEngine(size_t itmax, double tau, double epsilon1,
104  double epsilon2, double epsilon3, double delta)
105  : m_itmax{itmax}, m_opts{tau, epsilon1, epsilon2, epsilon3, delta} {
106 #ifdef LINSOLVERS_RETAIN_MEMORY
107  logger.warn() << "Using a non thread safe levmar! Parallelism will be reduced.";
108 #endif
109 }
110 
111 LevmarEngine::~LevmarEngine() = default;
112 
113 
114 #ifdef LINSOLVERS_RETAIN_MEMORY
115 // If the Levmar library is not configured for multithreading, this mutex is used to ensure only one thread
116 // at a time can enter levmar
117 namespace {
118  std::mutex levmar_mutex;
119 }
120 #endif
121 
123  ResidualEstimator& residual_estimator) {
124  // Create a tuple which keeps the references to the given manager and estimator
125  auto adata = std::tie(parameter_manager, residual_estimator);
126 
127  // The function which is called by the levmar loop
128  auto levmar_res_func = [](double *p, double *hx, int, int, void *extra) {
129 #ifdef LINSOLVERS_RETAIN_MEMORY
130  levmar_mutex.unlock();
131 #endif
132 
133  auto* extra_ptr = (decltype(adata)*)extra;
134  EngineParameterManager& pm = std::get<0>(*extra_ptr);
135  pm.updateEngineValues(p);
136  ResidualEstimator& re = std::get<1>(*extra_ptr);
137  re.populateResiduals(hx);
138 
139 #ifdef LINSOLVERS_RETAIN_MEMORY
140  levmar_mutex.lock();
141 #endif
142  };
143 
144  // Create the vector which will be used for keeping the parameter values
145  // and initialize it to the current values of the parameters
146  std::vector<double> param_values (parameter_manager.numberOfParameters());
147  parameter_manager.getEngineValues(param_values.begin());
148 
149  // Create a vector for getting the information of the minimization
150  std::array<double, 10> info = {0};
151 
152  std::vector<double> covariance_matrix (parameter_manager.numberOfParameters() * parameter_manager.numberOfParameters());
153 
154 #ifdef LINSOLVERS_RETAIN_MEMORY
155  levmar_mutex.lock();
156 #endif
157 
158  std::unique_ptr<double[]> workarea;
159  size_t workarea_size = LM_DIF_WORKSZ(parameter_manager.numberOfParameters(),
160  residual_estimator.numberOfResiduals());
161 
162  if (workarea_size <= LEVMAR_WORKAREA_MAX_SIZE / sizeof(double)) {
163  try {
164  workarea.reset(new double[workarea_size]);
165  } catch (const std::bad_alloc&) {
166  }
167  }
168 
169  // Didn't allocate
170  if (workarea == nullptr) {
171  LeastSquareSummary summary {};
173  summary.iteration_no = workarea_size;
174  summary.parameter_sigmas.resize(parameter_manager.numberOfParameters());
175  return summary;
176  }
177 
178  // Call the levmar library
179  auto start = std::chrono::steady_clock::now();
180  auto res = dlevmar_dif(levmar_res_func, // The function called from the levmar algorithm
181  param_values.data(), // The pointer where the parameter values are
182  NULL, // We don't use any measurement vector
183  parameter_manager.numberOfParameters(), // The number of free parameters
184  residual_estimator.numberOfResiduals(), // The number of residuals
185  m_itmax, // The maximum number of iterations
186  m_opts.data(), // The minimization options
187  info.data(), // Where the information of the minimization is stored
188  workarea.get(), // Working memory is allocated internally
189  covariance_matrix.data(),
190  &adata // No additional data needed
191  );
193  std::chrono::duration<float> elapsed = end - start;
194 #ifdef LINSOLVERS_RETAIN_MEMORY
195  levmar_mutex.unlock();
196 #endif
197 
198  // Create and return the summary object
199  LeastSquareSummary summary {};
200 
201  auto converted_covariance_matrix = parameter_manager.convertCovarianceMatrixToWorldSpace(covariance_matrix);
202  for (unsigned int i=0; i<parameter_manager.numberOfParameters(); i++) {
203  summary.parameter_sigmas.push_back(sqrt(converted_covariance_matrix[i*(parameter_manager.numberOfParameters()+1)]));
204  }
205 
206  summary.status_flag = getStatusFlag(info, res);
207  summary.engine_stop_reason = static_cast<int>(info[6]);
208  summary.iteration_no = static_cast<int>(info[5]);
209  summary.underlying_framework_info = info;
210  summary.duration = elapsed.count();
211  return summary;
212 }
213 
214 } // end of namespace ModelFitting
virtual ~LevmarEngine()
Destructor.
T tie(T...args)
Class containing the summary information of solving a least square minimization problem.
void populateResiduals(DoubleIter output_iter) const
void getEngineValues(DoubleIter output_iter) const
Returns the engine values of the managed parameters.
T end(T...args)
static Elements::Logging logger
void updateEngineValues(DoubleIter new_values_iter)
Updates the managed parameters with the given engine values.
T push_back(T...args)
T data(T...args)
void warn(const std::string &logMessage)
StatusFlag status_flag
Flag indicating if the minimization was successful.
static std::shared_ptr< LeastSquareEngine > createLevmarEngine(unsigned max_iterations)
std::vector< double > m_opts
Definition: LevmarEngine.h:72
#define LEVMAR_WORKAREA_MAX_SIZE
T reset(T...args)
LevmarEngine(size_t itmax=1000, double tau=1E-3, double epsilon1=1E-8, double epsilon2=1E-8, double epsilon3=1E-8, double delta=1E-4)
Constructs a new instance of the engine.
std::size_t numberOfResiduals() const
T get(T...args)
STL class.
__attribute__((noinline)) int backTrace(ELEMENTS_UNUSED std
static LeastSquareEngineManager::StaticEngine levmar_engine
STL class.
STL class.
Class responsible for managing the parameters the least square engine minimizes.
std::vector< double > convertCovarianceMatrixToWorldSpace(std::vector< double > covariance_matrix) const
T sqrt(T...args)
LeastSquareSummary solveProblem(EngineParameterManager &parameter_manager, ResidualEstimator &residual_estimator) override
Provides to the LeastSquareEngine the residual values.
static LeastSquareSummary::StatusFlag getStatusFlag(int ret)
Definition: GSLEngine.cpp:107
static Logging getLogger(const std::string &name="")
std::size_t numberOfParameters()
Returns the number of parameters managed by the manager.