/* * GslMinimizer.cpp, part of LatAnalyze * * Copyright (C) 2013 - 2020 Antonin Portelli * * LatAnalyze is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * LatAnalyze is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with LatAnalyze. If not, see . */ #include #include #include #include #include using namespace std; using namespace Latan; /****************************************************************************** * GslMinimizer implementation * ******************************************************************************/ // constructor ///////////////////////////////////////////////////////////////// GslMinimizer::GslMinimizer(const Algorithm algorithm) { setAlgorithm(algorithm); der_.setOrder(1, 1); } // access ////////////////////////////////////////////////////////////////////// GslMinimizer::Algorithm GslMinimizer::getAlgorithm(void) const { return algorithm_; } void GslMinimizer::setAlgorithm(const Algorithm algorithm) { algorithm_ = algorithm; } bool GslMinimizer::supportLimits(void) const { return false; } // test //////////////////////////////////////////////////////////////////////// bool GslMinimizer::isDerAlgorithm(const Algorithm algorithm) { return (algorithm <= Algorithm::lastDerAlg); } // minimization //////////////////////////////////////////////////////////////// const DVec & GslMinimizer::operator()(const DoubleFunction &f) { DVec &x = getState(); // resize minimizer state to match function number of arguments if (f.getNArg() != x.size()) { resize(f.getNArg()); } // set function data GslFuncData data; der_.setFunction(f); data.f = &f; data.d = &der_; // set initial position gsl_vector *gslX = gsl_vector_alloc(getDim()); for (Index i = 0; i < getDim(); ++i) { gsl_vector_set(gslX, i, x(i)); } // minimization int status; if (isDerAlgorithm(getAlgorithm())) { // set function gsl_multimin_function_fdf gslFunc; gslFunc.n = getDim(); gslFunc.f = &fWrapper; gslFunc.df = &dfWrapper; gslFunc.fdf = &fdfWrapper; gslFunc.params = &data; // create and set minimizer const gsl_multimin_fdfminimizer_type *gslAlg; gsl_multimin_fdfminimizer *gslMin; switch (getAlgorithm()) { case Algorithm::cgFR: gslAlg = gsl_multimin_fdfminimizer_conjugate_fr; break; case Algorithm::cgPR: gslAlg = gsl_multimin_fdfminimizer_conjugate_pr; break; case Algorithm::bfgs: gslAlg = gsl_multimin_fdfminimizer_vector_bfgs; break; case Algorithm::bfgs2: gslAlg = gsl_multimin_fdfminimizer_vector_bfgs2; break; case Algorithm::steepDesc: gslAlg = gsl_multimin_fdfminimizer_vector_bfgs2; break; default: LATAN_ERROR(Argument, "unknow GSL minization algorithm " + strFrom(static_cast(getAlgorithm()))); break; } gslMin = gsl_multimin_fdfminimizer_alloc(gslAlg, getDim()); // minimize unsigned int pass = 0, it; double dxRel; do { pass++; gsl_multimin_fdfminimizer_set(gslMin, &gslFunc, gslX, 0.01, 0.001); if (getVerbosity() >= Verbosity::Normal) { cout << "========== GSL minimization, pass #" << pass; cout << " ==========" << endl; cout << "Algorithm: " << getAlgorithmName(getAlgorithm()); cout << endl; cout << "Max eval.= " << getMaxIteration(); cout << " -- Precision= " << getPrecision() << endl; printf("Starting f(x)= %.10e\n", f(x)); } it = 0; do { it++; gsl_multimin_fdfminimizer_iterate(gslMin); dxRel = gsl_blas_dnrm2(gslMin->dx)/gsl_blas_dnrm2(gslMin->x); status = (dxRel < getPrecision()) ? GSL_SUCCESS : GSL_CONTINUE; if (getVerbosity() >= Verbosity::Debug) { printf("iteration %4d: f= %.10e dxrel= %.10e eval= %d\n", it, gslMin->f, dxRel, data.evalCount); } } while (status == GSL_CONTINUE and (data.evalCount < getMaxIteration())); if (getVerbosity() >= Verbosity::Normal) { printf("Found minimum %.10e at:\n", gslMin->f); for (Index i = 0; i < x.size(); ++i) { printf("%8s= %.10e\n", f.varName().getName(i).c_str(), gsl_vector_get(gslMin->x, i)); } cout << "after " << data.evalCount << " evaluations" << endl; cout << "Minimization ended with code " << status; cout << endl; } data.evalCount = 0; for (Index i = 0; i < getDim(); ++i) { gsl_vector_set(gslX, i, gsl_vector_get(gslMin->x, i)); } } while (status != GSL_SUCCESS and (pass < getMaxPass())); // deallocate GSL minimizer gsl_multimin_fdfminimizer_free(gslMin); } else { // set function gsl_multimin_function gslFunc; gslFunc.n = getDim(); gslFunc.f = &fWrapper; gslFunc.params = &data; // create and set minimizer const gsl_multimin_fminimizer_type *gslAlg; gsl_multimin_fminimizer *gslMin; switch (getAlgorithm()) { case Algorithm::simplex: gslAlg = gsl_multimin_fminimizer_nmsimplex; break; case Algorithm::simplex2: gslAlg = gsl_multimin_fminimizer_nmsimplex2; break; case Algorithm::simplex2R: gslAlg = gsl_multimin_fminimizer_nmsimplex2rand; break; default: LATAN_ERROR(Argument, "unknow GSL minization algorithm " + strFrom(static_cast(getAlgorithm()))); break; } gslMin = gsl_multimin_fminimizer_alloc(gslAlg, getDim()); // minimize unsigned int pass = 0, it; gsl_vector *step = gsl_vector_alloc(getDim()); double relSize; gsl_vector_set_all(step, 0.01); do { pass++; gsl_multimin_fminimizer_set(gslMin, &gslFunc, gslX, step); if (getVerbosity() >= Verbosity::Normal) { cout << "========== GSL minimization, pass #" << pass; cout << " ==========" << endl; cout << "Algorithm: " << getAlgorithmName(getAlgorithm()); cout << endl; cout << "Max eval.= " << getMaxIteration(); cout << " -- Precision= " << getPrecision() << endl; printf("Starting f(x)= %.10e\n", f(x)); } it = 0; do { it++; gsl_multimin_fminimizer_iterate(gslMin); relSize = Math::pow<2>(gslMin->size)/gsl_blas_dnrm2(gslMin->x); status = (relSize < getPrecision()) ? GSL_SUCCESS : GSL_CONTINUE; if (getVerbosity() >= Verbosity::Debug) { printf("iteration %4d: f= %.10e relSize= %.10e eval= %d\n", it, gslMin->fval, relSize, data.evalCount); } } while (status == GSL_CONTINUE and (data.evalCount < getMaxIteration())); if (getVerbosity() >= Verbosity::Normal) { printf("Found minimum %.10e at:\n", gslMin->fval); for (Index i = 0; i < x.size(); ++i) { printf("%8s= %.10e\n", f.varName().getName(i).c_str(), gsl_vector_get(gslMin->x, i)); } cout << "after " << data.evalCount << " evaluations" << endl; cout << "Minimization ended with code " << status; cout << endl; } data.evalCount = 0; for (Index i = 0; i < getDim(); ++i) { gsl_vector_set(gslX, i, gsl_vector_get(gslMin->x, i)); } } while (status != GSL_SUCCESS and (pass < getMaxPass())); // deallocate GSL minimizer gsl_multimin_fminimizer_free(gslMin); gsl_vector_free(step); } if (status != GSL_SUCCESS) { LATAN_WARNING("invalid minimum: maximum number of call reached"); } // save final result for (Index i = 0; i < getDim(); ++i) { x(i) = gsl_vector_get(gslX, i); } // deallocate GSL state and return gsl_vector_free(gslX); return x; } // function wrappers /////////////////////////////////////////////////////////// double GslMinimizer::fWrapper(const gsl_vector *x, void *vdata) { GslFuncData &data = *static_cast(vdata); data.evalCount++; return (*data.f)(x->data); } void GslMinimizer::dfWrapper(const gsl_vector *x, void *vdata, gsl_vector * df) { GslFuncData &data = *static_cast(vdata); const unsigned int n = data.f->getNArg(); for (unsigned int i = 0; i < n; ++i) { data.d->setDir(i); gsl_vector_set(df, i, (*(data.d))(x->data)); } data.evalCount += data.d->getNPoint()*n; } void GslMinimizer::fdfWrapper(const gsl_vector *x, void *vdata, double *f, gsl_vector * df) { GslFuncData &data = *static_cast(vdata); const unsigned int n = data.f->getNArg(); for (unsigned int i = 0; i < n; ++i) { data.d->setDir(i); gsl_vector_set(df, i, (*(data.d))(x->data)); } *f = (*data.f)(x->data); data.evalCount += data.d->getNPoint()*n + 1; } // algorithm names ///////////////////////////////////////////////////////////// string GslMinimizer::getAlgorithmName(const Algorithm algorithm) { switch (algorithm) { case Algorithm::cgFR: return "Fletcher-Reeves conjugate gradient"; break; case Algorithm::cgPR: return "Polak-Ribiere conjugate gradient"; break; case Algorithm::bfgs: return "Broyden-Fletcher-Goldfarb-Shanno"; break; case Algorithm::bfgs2: return "improved Broyden-Fletcher-Goldfarb-Shanno"; break; case Algorithm::steepDesc: return "steepest descent"; break; case Algorithm::simplex: return "Nelder-Mead simplex"; break; case Algorithm::simplex2: return "improved Nelder-Mead simplex"; break; case Algorithm::simplex2R: return "improved Nelder-Mead simplex with random start"; break; } return ""; }