Using the Lourakis constrained Levenberg-Marquardt library
Here is a very quickly prepared example of how to use the Lourakis library to do constrained minimisation in n-dimensions. This shows simple fitting of a second degree polynomial to a handful of observed points. The box constraint is used and it is set so that it does not contain the maximum likelihood point -- this shows off the constraint feature.
// Bojan Nikolic <bojan@bnikolic.co.uk>
//
// Example of how to use the Lourakis L-M library. Note this library
// is licensed under the GPL.
#include <vector>
#include <cmath>
#include <iostream>
#include <boost/assign/list_of.hpp>
#include "lm.h"
/** A toy second-degree polynomial model
*/
struct PolyModel {
/// The model parameters
double a, b, c;
/// These are the observation points, i.e., the points at which the
/// model should be evaluated
std::vector<double> x;
PolyModel(const std::vector<double> &x):
a(1.0),
b(2.0),
c(3.0),
x(x)
{
}
void operator() (std::vector<double> &res)
{
const size_t n=x.size();
res.resize(n);
for(size_t i=0; i<n; ++i)
res[i]=a+b*x[i]+c*pow(x[i],2);
}
};
/// This function interfaces between the Lourakis library and C++
/// code. It is hard coded for this particular problem. See BNMin1 for
/// a more general approach.
void LMHelper(double *p,
double *hx,
int m,
int n,
void *adata)
{
PolyModel *pmodel=reinterpret_cast<PolyModel*>(adata);
pmodel->a=p[0];
pmodel->b=p[1];
pmodel->c=p[2];
std::cout<<">> Interim point: "
<<p[0]<<","
<<p[1]<<","
<<p[2]<<std::endl;
std::vector<double> scratch;
(*pmodel)(scratch);
for (size_t i=0; i<n; ++i)
{
hx[i]=scratch[i];
}
}
void minimise(PolyModel &model,
std::vector<double> &obs,
std::vector<double> &lowerbounds,
std::vector<double> &upperbounds)
{
/// Options to the solver, see the manual of the library
double opts[]={0.1, 0.0001, 0.0001, 0.0001};
/// Output information from the solver
double info[9];
// This is the starting point of the minimisation. The finate
// difference doesn't work too great if it is started from 0,0,0
double ic[] = {0,0,1.0};
dlevmar_bc_dif(LMHelper,
ic,
&obs[0],
3,
obs.size(),
&lowerbounds[0],
&upperbounds[0],
1000,
opts,
info,
NULL,
NULL,
reinterpret_cast<void*>(&model)
);
std::cout<<"The final result is:"
<<ic[0]<<","
<<ic[1]<<","
<<ic[2]<<std::endl;
};
int main(void)
{
// This is the list of values of x at which the unknown function is
// observed
std::vector<double> x = boost::assign::list_of
(-10)(-8)(-5)(1)(4)(10)(40);
PolyModel model(x);
// The observed values corresponding to the x coordinates above. For
// this problem simply create mock data using the model itself
std::vector<double> obs;
model(obs);
std::cout<<"Simulated observation: ";
for (size_t i=0; i<obs.size(); ++i)
{
std::cout<<obs[i]<<",";
}
std::cout<<std::endl;
std::vector<double> lower = boost::assign::list_of
(-10)(-10)(-10);
// Make the upper bound of the first model parameter smaller than
// 1.0 to show off the constrained minimisation
std::vector<double> upper = boost::assign::list_of
(0.9)(10)(10);
// Run the minimiser
minimise(model,
obs,
lower,
upper);
}