pax_global_header00006660000000000000000000000064130555030650014514gustar00rootroot0000000000000052 comment=d231eac7b7e18f7c68852720c6d94dd9fab14575 CppNumericalSolvers-1.0.0/000077500000000000000000000000001305550306500154525ustar00rootroot00000000000000CppNumericalSolvers-1.0.0/.gitignore000066400000000000000000000015241305550306500174440ustar00rootroot00000000000000# CMakeFiles CMakeCache.txt CMakeFiles Makefile cmake_install.cmake install_manifest.txt CppNumericalSolvers.config # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app # subfolders /build /eigen /local # Matlab *.m~ *.mexa64 *.mexa32 *.mexw64 *.mexw32 *.mexmaci64 *.mexglx # Ignore backup files. *~ # Ignore Vim swap files. .*.swp # Ignore files generated by IDEs. /.classpath /.factorypath /.idea/ /.project /.settings /WORKSPACE.user.bzl /bazel.iml # Ignore all bazel-* symlinks. There is no full list since this can change # based on the name of the directory bazel is cloned into. /bazel-* # Ignore outputs generated during Bazel bootstrapping. /output/CppNumericalSolvers-1.0.0/.travis.yml000066400000000000000000000013521305550306500175640ustar00rootroot00000000000000dist: trusty addons: apt: sources: - ubuntu-toolchain-r-test packages: - wget - pkg-config - g++ - g++-4.9 - g++-5 - pkg-config - zip - zlib1g-dev - unzip before_install: - wget https://github.com/bazelbuild/bazel/releases/download/0.4.4/bazel-0.4.4-installer-linux-x86_64.sh - wget https://github.com/bazelbuild/bazel/releases/download/0.4.4/bazel-0.4.4-installer-linux-x86_64.sh.sha256 - sha256sum -c bazel-0.4.4-installer-linux-x86_64.sh.sha256 - chmod +x bazel-0.4.4-installer-linux-x86_64.sh - ./bazel-0.4.4-installer-linux-x86_64.sh --user - export PATH="$PATH:$HOME/bin" script: - bazel run verify - python lint.py notifications: email: false CppNumericalSolvers-1.0.0/BUILD000066400000000000000000000007461305550306500162430ustar00rootroot00000000000000package(default_visibility = ["//visibility:public"]) load("//:generator.bzl", "build_example", "build_test") build_example("linearregression") build_example("logisticregression") build_example("rosenbrock") build_example("rosenbrock_float") build_example("simple") build_example("simple_withoptions") build_example("neldermead") build_example("neldermead-customized") build_test("verify") py_binary( name = "lint", args = ["src/test/verify.cpp"], srcs = ["lint.py"], )CppNumericalSolvers-1.0.0/LICENSE000066400000000000000000000021661305550306500164640ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014-2015 Patrick Wieschollek Copyright (c) 2015- the respective contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.CppNumericalSolvers-1.0.0/README.md000066400000000000000000000171211305550306500167330ustar00rootroot00000000000000CppOptimizationLibrary ================================================================= [![Build Status](https://api.travis-ci.org/PatWie/CppNumericalSolvers.svg?branch=master)](http://travis-ci.org/PatWie/CppNumericalSolvers) Get this Library ----------- ````bash git clone https://github.com/PatWie/CppNumericalSolvers.git cd CppNumericalSolvers/ bazel run verify ```` About ----------- Have you ever looked for a c++ version of MATLAB's *fminsearch*, which is easy to use without adding tons of dependencies and without editing many setting-structs? This project exactly addresses this issue by providing a *header-only* library without dependencies. All solvers are written from scratch, which means they do not represent the current state-of-the-art implementation with all tricky optimizations (at least for now). But they are very easy to use. Want a full example? ````cpp class Rosenbrock : public Problem { public: double value(const Vector &x) { const double t1 = (1 - x[0]); const double t2 = (x[1] - x[0] * x[0]); return t1 * t1 + 100 * t2 * t2; } }; int main(int argc, char const *argv[]) { Rosenbrock f; Vector x(2); x << -1, 2; BfgsSolver solver; solver.minimize(f, x); std::cout << "argmin " << x.transpose() << std::endl; std::cout << "f in argmin " << f(x) << std::endl; return 0; } ```` To use another solver, simply replace `BfgsSolver` by another name. Supported solvers are: - gradient descent solver (GradientDescentSolver) - conjugate gradient descent solver (ConjugatedGradientDescentSolver) - Newton descent solver (NewtonDescentSolver) - BFGS solver (BfgsSolver) - L-BFGS solver (LbfgsSolver) - L-BFGS-B solver (LbfgsbSolver) - CMAes solver (CMAesSolver) - Nelder-Mead solver (NelderMeadSolver) These solvers are tested on the Rosenbrock function from multiple difficult starting points by unit tests using the Google Testing Framework. And yes, you can use them directly in MATLAB. Additional benchmark functions are *Beale, GoldsteinPrice, Booth, Matyas, Levi*. Note, not all solvers are equivalently good at all problems. For checking your gradient this library uses high-order central difference. Study the examples for more information about including box-constraints and gradient-information. Install ----------- Note, this library is header-only, so you just need to add `include/cppoptlib` to your project without compiling anything and without adding further dependencies. We ship some examples for demonstration purposes and use [bazel](https://bazel.build/) to compile these examples and unittests. The latest commit using CMake is da314c6581d076e0dbadacdd263aefe4d06a2397. When using the MATLAB-binding, you need to compile the mex-file. Hereby, open Matlab and run `make.m` inside the MATLAB folder once. Extensive Introduction ----------- There are currently two ways to use this library: directly in your C++ code or in MATLAB by calling the provided mex-File. ## C++ There are several examples within the `src/examples` directory. These are built into `build/bin/examples` during `make all`. Checkout `rosenbrock.cpp`. Your objective and gradient computations should be stored into a tiny class. The most simple usage is ````cpp class YourProblem : public Problem { double value(const Vector &x) {} } ```` In contrast to previous versions of this library, I switched to classes instead of lambda function. If you poke the examples, you will notice that this is much easier to write and understand. The only method a problem has to provide is the `value` member, which returns the value of the objective function. For most solvers it should be useful to implement the gradient computation, too. Otherwise the library internally will use finite difference for gradient computations (which is definitely unstable and slow!). ````cpp class YourProblem : public Problem { double value(const Vector &x) {} void gradient(const Vector &x, Vector &grad) {} } ```` Notice, the gradient is passed by reference! After defining the problem it can be initialized in your code by: ````cpp // init problem YourProblem f; // test your gradient ONCE bool probably_correct = f.checkGradient(x); ```` By convention, a solver minimizes a given objective function starting in `x` ````cpp // choose a solver BfgsSolver solver; // and minimize the function solver.minimize(f, x); double minValue = f(x); ```` For convenience there are some typedefs: ````cpp cppoptlib::Vector is a column vector Eigen::Matrix; cppoptlib::Matrix is a matrix Eigen::Matrix; ```` ### full example ````cpp #include #include "../../include/cppoptlib/meta.h" #include "../../include/cppoptlib/problem.h" #include "../../include/cppoptlib/solver/bfgssolver.h" using namespace cppoptlib; using Eigen::VectorXd; class Rosenbrock : public Problem { public: using typename cppoptlib::Problem::Scalar; using typename cppoptlib::Problem::TVector; double value(const TVector &x) { const double t1 = (1 - x[0]); const double t2 = (x[1] - x[0] * x[0]); return t1 * t1 + 100 * t2 * t2; } void gradient(const TVector &x, TVector &grad) { grad[0] = -2 * (1 - x[0]) + 200 * (x[1] - x[0] * x[0]) * (-2 * x[0]); grad[1] = 200 * (x[1] - x[0] * x[0]); } }; int main(int argc, char const *argv[]) { Rosenbrock f; BfgsSolver solver; VectorXd x(2); x << -1, 2; solver.minimize(f, x); std::cout << "argmin " << x.transpose() << std::endl; std::cout << "f in argmin " << f(x) << std::endl; return 0; } ```` ### using box-constraints The `L-BFGS-B` algorithm allows to optimize functions with box-constraints, i.e., `min_x f(x) s.t. a <= x <= b` for some `a, b`. Given a `problem`-class you can enter these constraints by ````cpp cppoptlib::YourProblem f; f.setLowerBound(Vector::Zero(DIM)); f.setUpperBound(Vector::Ones(DIM)*5); ```` If you do not specify a bound, the algorithm will assume the unbounded case, eg. ````cpp cppoptlib::YourProblem f; f.setLowerBound(Vector::Zero(DIM)); ```` will optimize in x s.t. `0 <= x`. ## within MATLAB Simply create a function file for the objective and replace `fminsearch` or `fminunc` with `cppoptlib`. If you want to use symbolic gradient or hessian information see file `example.m` for details. A basic example would be: ````matlab x0 = [-1,2]'; [fx,x] = cppoptlib(x0, @rosenbrock,'gradient',@rosenbrock_grad,'solver','bfgs'); fx = cppoptlib(x0, @rosenbrock); fx = fminsearch(x0, @rosenbrock); ```` Even optimizing without any gradient information this library outperforms optimization routines from MATLAB on some problems. # Benchmarks Currently, not all solvers are equally good at all objective functions. The file `src/test/benchmark.cpp` contains some challenging objective functions which are tested by each provided solver. Note, MATLAB will also fail at some objective functions. # Contribute Make sure that `python lint.py` does not display any errors and check if travis is happy. It would be great, if some of the Forks-Owner are willing to make pull-request. [eigen3]: http://eigen.tuxfamily.org/ [bazel]: https://bazel.build/ [matlab]: http://www.mathworks.de/products/matlab/ CppNumericalSolvers-1.0.0/WORKSPACE000066400000000000000000000007151305550306500167360ustar00rootroot00000000000000new_http_archive( name = "eigen_archive", url = "https://bitbucket.org/eigen/eigen/get/3.2.10.tar.gz", build_file = "eigen.BUILD", strip_prefix = "eigen-eigen-b9cd8366d4e8", ) new_http_archive( name = "gtest", url = "https://github.com/google/googletest/archive/release-1.7.0.zip", sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0", build_file = "gtest.BUILD", strip_prefix = "googletest-release-1.7.0", )CppNumericalSolvers-1.0.0/bazel.rc000066400000000000000000000013321305550306500170740ustar00rootroot00000000000000# This is from Bazel's former travis setup, to avoid blowing up the RAM usage. startup --host_jvm_args=-Xmx2500m startup --host_jvm_args=-Xms2500m startup --batch test --ram_utilization_factor=10 # This is so we understand failures better build --verbose_failures # This is so we don't use sandboxed execution. Sandboxed execution # runs stuff in a container, and since Travis already runs its script # in a container (unless you require sudo in your .travis.yml) this # fails to run tests. build --spawn_strategy=standalone --genrule_strategy=standalone test --test_strategy=standalone # Below this line, .travis.yml will cat the default bazelrc. # This is needed so Bazel starts with the base workspace in its # package path.CppNumericalSolvers-1.0.0/eigen.BUILD000066400000000000000000000004361305550306500173250ustar00rootroot00000000000000# Description: # Eigen is a C++ template library for linear algebra: vectors, # matrices, and related algorithms. cc_library( name = "eigen3", srcs = glob(['Eigen/src/**/*.h']), includes = ['.'], hdrs = glob(['Eigen/*']), visibility = ["//visibility:public"], )CppNumericalSolvers-1.0.0/generator.bzl000066400000000000000000000007631305550306500201570ustar00rootroot00000000000000def build_example(name, visibility=None): native.cc_binary( name = name, srcs = ["src/examples/"+name+".cpp"], deps = [ "//include/cppoptlib:cppoptlib", "@eigen_archive//:eigen3" ] ) def build_test(name, visibility=None): native.cc_test( name = name, srcs = ["src/test/"+name+".cpp"], copts = ["-Iexternal/gtest/include"], deps = [ "//include/cppoptlib:cppoptlib", "@eigen_archive//:eigen3", "@gtest//:main" ] ) CppNumericalSolvers-1.0.0/gtest.BUILD000066400000000000000000000004541305550306500173640ustar00rootroot00000000000000cc_library( name = "main", srcs = glob( ["src/*.cc"], exclude = ["src/gtest-all.cc"] ), hdrs = glob([ "include/**/*.h", "src/*.h" ]), copts = ["-Iexternal/gtest/include"], linkopts = ["-pthread"], visibility = ["//visibility:public"], )CppNumericalSolvers-1.0.0/include/000077500000000000000000000000001305550306500170755ustar00rootroot00000000000000CppNumericalSolvers-1.0.0/include/cppoptlib/000077500000000000000000000000001305550306500210715ustar00rootroot00000000000000CppNumericalSolvers-1.0.0/include/cppoptlib/BUILD000066400000000000000000000002511305550306500216510ustar00rootroot00000000000000cc_library( name = "cppoptlib", hdrs = glob([ "*.h", "solver/*.h", "linesearch/*.h", ]), visibility = ["//visibility:public"], ) CppNumericalSolvers-1.0.0/include/cppoptlib/boundedproblem.h000066400000000000000000000027261305550306500242520ustar00rootroot00000000000000/* * This file is part of CppNumericalSolvers * * Copyright (C) Tobias Wood @spinicist 2016 * * This Source Code form is subject to the terms of the MIT license. * Please see the LICENSE file. * */ #ifndef BOUNDEDPROBLEM_H #define BOUNDEDPROBLEM_H #include #include #include "problem.h" namespace cppoptlib { template class BoundedProblem : public Problem { public: using Superclass = Problem; using typename Superclass::Scalar; using typename Superclass::TVector; protected: TVector m_lowerBound; TVector m_upperBound; public: BoundedProblem(int RunDim = CompileDim_) : Superclass() { TVector infBound(RunDim); infBound.setConstant(std::numeric_limits::infinity()); m_lowerBound = -infBound; m_upperBound = infBound; } BoundedProblem(const TVector &l, const TVector &u) : Superclass(), m_lowerBound(l), m_upperBound(u) {} const TVector &lowerBound() const { return m_lowerBound; } void setLowerBound(const TVector &lb) { m_lowerBound = lb; } const TVector &upperBound() const { return m_upperBound; } void setUpperBound(const TVector &ub) { m_upperBound = ub; } void setBoxConstraint(TVector lb, TVector ub) { setLowerBound(lb); setUpperBound(ub); } }; } // end namespace cppoptlib #endif // BOUNDEDPROBLEM_H CppNumericalSolvers-1.0.0/include/cppoptlib/linesearch/000077500000000000000000000000001305550306500232065ustar00rootroot00000000000000CppNumericalSolvers-1.0.0/include/cppoptlib/linesearch/armijo.h000066400000000000000000000044401305550306500246420ustar00rootroot00000000000000// CppNumericalSolver #ifndef ARMIJO_H_ #define ARMIJO_H_ #include "../meta.h" namespace cppoptlib { template class Armijo { public: using Scalar = typename ProblemType::Scalar; using TVector = typename ProblemType::TVector; /** * @brief use Armijo Rule for (weak) Wolfe conditiions * @details [long description] * * @param searchDir search direction for next update step * @param objFunc handle to problem * * @return step-width */ static Scalar linesearch(const TVector &x, const TVector &searchDir, ProblemType &objFunc, const Scalar alpha_init = 1.0) { const Scalar c = 0.2; const Scalar rho = 0.9; Scalar alpha = alpha_init; Scalar f = objFunc.value(x + alpha * searchDir); const Scalar f_in = objFunc.value(x); TVector grad(x.rows()); objFunc.gradient(x, grad); const Scalar Cache = c * grad.dot(searchDir); while(f > f_in + alpha * Cache) { alpha *= rho; f = objFunc.value(x + alpha * searchDir); } return alpha; } }; template class Armijo { public: using typename ProblemType::Scalar; using typename ProblemType::TVector; using typename ProblemType::THessian; /** * @brief use Armijo Rule for (weak) Wolfe conditiions * @details [long description] * * @param searchDir search direction for next update step * @param objFunc handle to problem * * @return step-width */ static Scalar linesearch(const TVector &x, const TVector &searchDir, ProblemType &objFunc) { const Scalar c = 0.2; const Scalar rho = 0.9; Scalar alpha = 1.0; Scalar f = objFunc.value(x + alpha * searchDir); const Scalar f_in = objFunc.value(x); const THessian hessian(x.rows(), x.rows()); objFunc.hessian(x, hessian); TVector grad(x.rows()); objFunc.gradient(x, grad); const Scalar Cache = c * grad.dot(searchDir) + 0.5 * c*c * searchDir.transpose() * (hessian * searchDir); while(f > f_in + alpha * Cache) { alpha *= rho; f = objFunc.value(x + alpha * searchDir); } return alpha; } }; } #endif /* ARMIJO_H_ */ CppNumericalSolvers-1.0.0/include/cppoptlib/linesearch/morethuente.h000066400000000000000000000202671305550306500257250ustar00rootroot00000000000000// CppNumericalSolver #ifndef MORETHUENTE_H_ #define MORETHUENTE_H_ #include "../meta.h" #include namespace cppoptlib { template class MoreThuente { public: using Scalar = typename ProblemType::Scalar; using TVector = typename ProblemType::TVector; /** * @brief use MoreThuente Rule for (strong) Wolfe conditiions * @details [long description] * * @param searchDir search direction for next update step * @param objFunc handle to problem * * @return step-width */ static Scalar linesearch(const TVector &x, const TVector &searchDir, ProblemType &objFunc, const Scalar alpha_init = 1.0) { // assume step width Scalar ak = alpha_init; Scalar fval = objFunc.value(x); TVector g = x.eval(); objFunc.gradient(x, g); TVector s = searchDir.eval(); TVector xx = x.eval(); cvsrch(objFunc, xx, fval, g, ak, s); return ak; } static int cvsrch(ProblemType &objFunc, TVector &x, Scalar f, TVector &g, Scalar &stp, TVector &s) { // we rewrite this from MIN-LAPACK and some MATLAB code int info = 0; int infoc = 1; const Scalar xtol = 1e-15; const Scalar ftol = 1e-4; const Scalar gtol = 1e-2; const Scalar stpmin = 1e-15; const Scalar stpmax = 1e15; const Scalar xtrapf = 4; const int maxfev = 20; int nfev = 0; Scalar dginit = g.dot(s); if (dginit >= 0.0) { // no descent direction // TODO: handle this case return -1; } bool brackt = false; bool stage1 = true; Scalar finit = f; Scalar dgtest = ftol * dginit; Scalar width = stpmax - stpmin; Scalar width1 = 2 * width; TVector wa = x.eval(); Scalar stx = 0.0; Scalar fx = finit; Scalar dgx = dginit; Scalar sty = 0.0; Scalar fy = finit; Scalar dgy = dginit; Scalar stmin; Scalar stmax; while (true) { // make sure we stay in the interval when setting min/max-step-width if (brackt) { stmin = std::min(stx, sty); stmax = std::max(stx, sty); } else { stmin = stx; stmax = stp + xtrapf * (stp - stx); } // Force the step to be within the bounds stpmax and stpmin. stp = std::max(stp, stpmin); stp = std::min(stp, stpmax); // Oops, let us return the last reliable values if ( (brackt && ((stp <= stmin) || (stp >= stmax))) || (nfev >= maxfev - 1 ) || (infoc == 0) || (brackt && ((stmax - stmin) <= (xtol * stmax)))) { stp = stx; } // test new point x = wa + stp * s; f = objFunc.value(x); objFunc.gradient(x, g); nfev++; Scalar dg = g.dot(s); Scalar ftest1 = finit + stp * dgtest; // all possible convergence tests if ((brackt & ((stp <= stmin) | (stp >= stmax))) | (infoc == 0)) info = 6; if ((stp == stpmax) & (f <= ftest1) & (dg <= dgtest)) info = 5; if ((stp == stpmin) & ((f > ftest1) | (dg >= dgtest))) info = 4; if (nfev >= maxfev) info = 3; if (brackt & (stmax - stmin <= xtol * stmax)) info = 2; if ((f <= ftest1) & (fabs(dg) <= gtol * (-dginit))) info = 1; // terminate when convergence reached if (info != 0) return -1; if (stage1 & (f <= ftest1) & (dg >= std::min(ftol, gtol)*dginit)) stage1 = false; if (stage1 & (f <= fx) & (f > ftest1)) { Scalar fm = f - stp * dgtest; Scalar fxm = fx - stx * dgtest; Scalar fym = fy - sty * dgtest; Scalar dgm = dg - dgtest; Scalar dgxm = dgx - dgtest; Scalar dgym = dgy - dgtest; cstep( stx, fxm, dgxm, sty, fym, dgym, stp, fm, dgm, brackt, stmin, stmax, infoc); fx = fxm + stx * dgtest; fy = fym + sty * dgtest; dgx = dgxm + dgtest; dgy = dgym + dgtest; } else { // this is ugly and some variables should be moved to the class scope cstep( stx, fx, dgx, sty, fy, dgy, stp, f, dg, brackt, stmin, stmax, infoc); } if (brackt) { if (fabs(sty - stx) >= 0.66 * width1) stp = stx + 0.5 * (sty - stx); width1 = width; width = fabs(sty - stx); } } return 0; } static int cstep(Scalar& stx, Scalar& fx, Scalar& dx, Scalar& sty, Scalar& fy, Scalar& dy, Scalar& stp, Scalar& fp, Scalar& dp, bool& brackt, Scalar& stpmin, Scalar& stpmax, int& info) { info = 0; bool bound = false; // Check the input parameters for errors. if ((brackt & ((stp <= std::min(stx, sty) ) | (stp >= std::max(stx, sty)))) | (dx * (stp - stx) >= 0.0) | (stpmax < stpmin)) { return -1; } Scalar sgnd = dp * (dx / fabs(dx)); Scalar stpf = 0; Scalar stpc = 0; Scalar stpq = 0; if (fp > fx) { info = 1; bound = true; Scalar theta = 3. * (fx - fp) / (stp - stx) + dx + dp; Scalar s = std::max(theta, std::max(dx, dp)); Scalar gamma = s * sqrt((theta / s) * (theta / s) - (dx / s) * (dp / s)); if (stp < stx) gamma = -gamma; Scalar p = (gamma - dx) + theta; Scalar q = ((gamma - dx) + gamma) + dp; Scalar r = p / q; stpc = stx + r * (stp - stx); stpq = stx + ((dx / ((fx - fp) / (stp - stx) + dx)) / 2.) * (stp - stx); if (fabs(stpc - stx) < fabs(stpq - stx)) stpf = stpc; else stpf = stpc + (stpq - stpc) / 2; brackt = true; } else if (sgnd < 0.0) { info = 2; bound = false; Scalar theta = 3 * (fx - fp) / (stp - stx) + dx + dp; Scalar s = std::max(theta, std::max(dx, dp)); Scalar gamma = s * sqrt((theta / s) * (theta / s) - (dx / s) * (dp / s)); if (stp > stx) gamma = -gamma; Scalar p = (gamma - dp) + theta; Scalar q = ((gamma - dp) + gamma) + dx; Scalar r = p / q; stpc = stp + r * (stx - stp); stpq = stp + (dp / (dp - dx)) * (stx - stp); if (fabs(stpc - stp) > fabs(stpq - stp)) stpf = stpc; else stpf = stpq; brackt = true; } else if (fabs(dp) < fabs(dx)) { info = 3; bound = 1; Scalar theta = 3 * (fx - fp) / (stp - stx) + dx + dp; Scalar s = std::max(theta, std::max( dx, dp)); Scalar gamma = s * sqrt(std::max(static_cast(0.), (theta / s) * (theta / s) - (dx / s) * (dp / s))); if (stp > stx) gamma = -gamma; Scalar p = (gamma - dp) + theta; Scalar q = (gamma + (dx - dp)) + gamma; Scalar r = p / q; if ((r < 0.0) & (gamma != 0.0)) { stpc = stp + r * (stx - stp); } else if (stp > stx) { stpc = stpmax; } else { stpc = stpmin; } stpq = stp + (dp / (dp - dx)) * (stx - stp); if (brackt) { if (fabs(stp - stpc) < fabs(stp - stpq)) { stpf = stpc; } else { stpf = stpq; } } else { if (fabs(stp - stpc) > fabs(stp - stpq)) { stpf = stpc; } else { stpf = stpq; } } } else { info = 4; bound = false; if (brackt) { Scalar theta = 3 * (fp - fy) / (sty - stp) + dy + dp; Scalar s = std::max(theta, std::max(dy, dp)); Scalar gamma = s * sqrt((theta / s) * (theta / s) - (dy / s) * (dp / s)); if (stp > sty) gamma = -gamma; Scalar p = (gamma - dp) + theta; Scalar q = ((gamma - dp) + gamma) + dy; Scalar r = p / q; stpc = stp + r * (sty - stp); stpf = stpc; } else if (stp > stx) stpf = stpmax; else { stpf = stpmin; } } if (fp > fx) { sty = stp; fy = fp; dy = dp; } else { if (sgnd < 0.0) { sty = stx; fy = fx; dy = dx; } stx = stp; fx = fp; dx = dp; } stpf = std::min(stpmax, stpf); stpf = std::max(stpmin, stpf); stp = stpf; if (brackt & bound) { if (sty > stx) { stp = std::min(stx + static_cast(0.66) * (sty - stx), stp); } else { stp = std::max(stx + static_cast(0.66) * (sty - stx), stp); } } return 0; } }; } #endif /* MORETHUENTE_H_ */ CppNumericalSolvers-1.0.0/include/cppoptlib/linesearch/wolfeheuristic.h000066400000000000000000000033631305550306500264200ustar00rootroot00000000000000// CppNumericalSolver #ifndef WOLFERULE_H_ #define WOLFERULE_H_ #include "../meta.h" namespace cppoptlib { /** * @brief this tries to guess the correct stepwith in a bisection-style * @details WARNING: THIS IS QUITE HACKY. TEST ARMIJO before. * * @tparam T scalar type * @tparam P problem type * @tparam Ord order of solver */ template class WolfeHeuristic { public: static T linesearch(const Vector & x0, const Vector & searchDir, P &objFunc, const T alpha_init = 1) { Vector x = x0; // evaluate phi(0) T phi0 = objFunc.value(x0); // evaluate phi'(0) Vector grad(x.rows()); objFunc.gradient(x, grad); T phi0_dash = searchDir.dot(grad); T alpha = alpha_init; bool decrease_direction = true; // 200 guesses for(size_t iter = 0; iter < 200; ++iter) { // new guess for phi(alpha) Vector x_candidate = x + alpha * searchDir; const T phi = objFunc.value(x_candidate); // decrease condition invalid --> shrink interval if (phi > phi0 + 0.0001 * alpha * phi0_dash) { alpha *= 0.5; decrease_direction = false; } else { // valid decrease --> test strong wolfe condition Vector grad2(x.rows()); objFunc.gradient(x_candidate, grad2); const T phi_dash = searchDir.dot(grad2); // curvature condition invalid ? if ((phi_dash < 0.9 * phi0_dash) || !decrease_direction) { // increase interval alpha *= 4.0; } else { // both condition are valid --> we are happy x = x_candidate; grad = grad2; phi0 = phi; return alpha; } } } return alpha; } }; } #endif /* WOLFERULE_H_ */ CppNumericalSolvers-1.0.0/include/cppoptlib/meta.h000066400000000000000000000101741305550306500221730ustar00rootroot00000000000000// CppNumericalSolver #ifndef META_H #define META_H #include #include #include namespace cppoptlib { /*template using Vector = Eigen::Matrix; template using Matrix = Eigen::Matrix; */ enum class DebugLevel { None = 0, Low, High }; enum class Status { NotStarted = -1, Continue = 0, IterationLimit, XDeltaTolerance, FDeltaTolerance, GradNormTolerance, Condition, UserDefined }; enum class SimplexOp { Place, Reflect, Expand, ContractIn, ContractOut, Shrink }; inline std::ostream &operator<<(std::ostream &os, const SimplexOp &op) { switch (op) { case SimplexOp::Place: os << "place"; break; case SimplexOp::Reflect: os << "reflect"; break; case SimplexOp::Expand: os << "expand"; break; case SimplexOp::ContractIn: os << "contract-in"; break; case SimplexOp::ContractOut: os << "contract-out"; break; case SimplexOp::Shrink: os << "shrink"; break; } return os; } inline std::string op_to_string(SimplexOp op) { switch (op) { case SimplexOp::Place: return "place"; case SimplexOp::Expand: return "expand"; case SimplexOp::Reflect: return "reflect"; case SimplexOp::ContractIn: return "contract-in"; case SimplexOp::ContractOut: return "contract-out"; case SimplexOp::Shrink: return "shrink"; } return "unknown"; } template class Criteria { public: size_t iterations; //!< Maximum number of iterations T xDelta; //!< Minimum change in parameter vector T fDelta; //!< Minimum change in cost function T gradNorm; //!< Minimum norm of gradient vector T condition; //!< Maximum condition number of Hessian Criteria() { reset(); } static Criteria defaults() { Criteria d; d.iterations = 10000; d.xDelta = 0; d.fDelta = 0; d.gradNorm = 1e-4; d.condition = 0; return d; } void reset() { iterations = 0; xDelta = 0; fDelta = 0; gradNorm = 0; condition = 0; } void print(std::ostream &os) const { os << "Iterations: " << iterations << std::endl; os << "xDelta: " << xDelta << std::endl; os << "fDelta: " << fDelta << std::endl; os << "GradNorm: " << gradNorm << std::endl; os << "Condition: " << condition << std::endl; } }; template Status checkConvergence(const Criteria &stop, const Criteria ¤t) { if ((stop.iterations > 0) && (current.iterations > stop.iterations)) { return Status::IterationLimit; } if ((stop.xDelta > 0) && (current.xDelta < stop.xDelta)) { return Status::XDeltaTolerance; } if ((stop.fDelta > 0) && (current.fDelta < stop.fDelta)) { return Status::FDeltaTolerance; } if ((stop.gradNorm > 0) && (current.gradNorm < stop.gradNorm)) { return Status::GradNormTolerance; } if ((stop.condition > 0) && (current.condition > stop.condition)) { return Status::Condition; } return Status::Continue; } inline std::ostream &operator<<(std::ostream &os, const Status &s) { switch (s) { case Status::NotStarted: os << "Solver not started."; break; case Status::Continue: os << "Convergence criteria not reached."; break; case Status::IterationLimit: os << "Iteration limit reached."; break; case Status::XDeltaTolerance: os << "Change in parameter vector too small."; break; case Status::FDeltaTolerance: os << "Change in cost function value too small."; break; case Status::GradNormTolerance: os << "Gradient vector norm too small."; break; case Status::Condition: os << "Condition of Hessian/Covariance matrix too large."; break; case Status::UserDefined: os << "Stop condition defined in the callback."; break; } return os; } template std::ostream &operator<<(std::ostream &os, const Criteria &c) { c.print(os); return os; } } // End namespace cppoptlib #endif /* META_H */ CppNumericalSolvers-1.0.0/include/cppoptlib/problem.h000066400000000000000000000164161305550306500227120ustar00rootroot00000000000000#ifndef PROBLEM_H #define PROBLEM_H #include #include #include #include "meta.h" namespace cppoptlib { template class Problem { public: static const int Dim = Dim_; typedef Scalar_ Scalar; using TVector = Eigen::Matrix; using THessian = Eigen::Matrix; using TCriteria = Criteria; using TIndex = typename TVector::Index; using MatrixType = Eigen::Matrix; public: Problem() {} virtual ~Problem()= default; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" virtual bool callback(const Criteria &state, const TVector &x) { return true; } virtual bool detailed_callback(const Criteria &state, SimplexOp op, int index, const MatrixType &x, std::vector f) { return true; } #pragma GCC diagnostic pop /** * @brief returns objective value in x * @details [long description] * * @param x [description] * @return [description] */ virtual Scalar value(const TVector &x) = 0; /** * @brief overload value for nice syntax * @details [long description] * * @param x [description] * @return [description] */ Scalar operator()(const TVector &x) { return value(x); } /** * @brief returns gradient in x as reference parameter * @details should be overwritten by symbolic gradient * * @param grad [description] */ virtual void gradient(const TVector &x, TVector &grad) { finiteGradient(x, grad); } /** * @brief This computes the hessian * @details should be overwritten by symbolic hessian, if solver relies on hessian */ virtual void hessian(const TVector &x, THessian &hessian) { finiteHessian(x, hessian); } virtual bool checkGradient(const TVector &x, int accuracy = 3) { // TODO: check if derived class exists: // int(typeid(&Rosenbrock::gradient) == typeid(&Problem::gradient)) == 1 --> overwritten const TIndex D = x.rows(); TVector actual_grad(D); TVector expected_grad(D); gradient(x, actual_grad); finiteGradient(x, expected_grad, accuracy); for (TIndex d = 0; d < D; ++d) { Scalar scale = std::max(static_cast(std::max(fabs(actual_grad[d]), fabs(expected_grad[d]))), Scalar(1.)); if(fabs(actual_grad[d]-expected_grad[d])>1e-2 * scale) return false; } return true; } virtual bool checkHessian(const TVector &x, int accuracy = 3) { // TODO: check if derived class exists: // int(typeid(&Rosenbrock::gradient) == typeid(&Problem::gradient)) == 1 --> overwritten const TIndex D = x.rows(); THessian actual_hessian = THessian::Zero(D, D); THessian expected_hessian = THessian::Zero(D, D); hessian(x, actual_hessian); finiteHessian(x, expected_hessian, accuracy); for (TIndex d = 0; d < D; ++d) { for (TIndex e = 0; e < D; ++e) { Scalar scale = std::max(static_cast(std::max(fabs(actual_hessian(d, e)), fabs(expected_hessian(d, e)))), Scalar(1.)); if(fabs(actual_hessian(d, e)- expected_hessian(d, e))>1e-1 * scale) return false; } } return true; } void finiteGradient(const TVector &x, TVector &grad, int accuracy = 0) { // accuracy can be 0, 1, 2, 3 const Scalar eps = 2.2204e-6; static const std::array, 4> coeff = { { {1, -1}, {1, -8, 8, -1}, {-1, 9, -45, 45, -9, 1}, {3, -32, 168, -672, 672, -168, 32, -3} } }; static const std::array, 4> coeff2 = { { {1, -1}, {-2, -1, 1, 2}, {-3, -2, -1, 1, 2, 3}, {-4, -3, -2, -1, 1, 2, 3, 4} } }; static const std::array dd = {2, 12, 60, 840}; grad.resize(x.rows()); TVector& xx = const_cast(x); const int innerSteps = 2*(accuracy+1); const Scalar ddVal = dd[accuracy]*eps; for (TIndex d = 0; d < x.rows(); d++) { grad[d] = 0; for (int s = 0; s < innerSteps; ++s) { Scalar tmp = xx[d]; xx[d] += coeff2[accuracy][s]*eps; grad[d] += coeff[accuracy][s]*value(xx); xx[d] = tmp; } grad[d] /= ddVal; } } void finiteHessian(const TVector &x, THessian &hessian, int accuracy = 0) { const Scalar eps = std::numeric_limits::epsilon()*10e7; hessian.resize(x.rows(), x.rows()); TVector& xx = const_cast(x); if(accuracy == 0) { for (TIndex i = 0; i < x.rows(); i++) { for (TIndex j = 0; j < x.rows(); j++) { Scalar tmpi = xx[i]; Scalar tmpj = xx[j]; Scalar f4 = value(xx); xx[i] += eps; xx[j] += eps; Scalar f1 = value(xx); xx[j] -= eps; Scalar f2 = value(xx); xx[j] += eps; xx[i] -= eps; Scalar f3 = value(xx); hessian(i, j) = (f1 - f2 - f3 + f4) / (eps * eps); xx[i] = tmpi; xx[j] = tmpj; } } } else { /* \displaystyle{{\frac{\partial^2{f}}{\partial{x}\partial{y}}}\approx \frac{1}{600\,h^2} \left[\begin{matrix} -63(f_{1,-2}+f_{2,-1}+f_{-2,1}+f_{-1,2})+\\ 63(f_{-1,-2}+f_{-2,-1}+f_{1,2}+f_{2,1})+\\ 44(f_{2,-2}+f_{-2,2}-f_{-2,-2}-f_{2,2})+\\ 74(f_{-1,-1}+f_{1,1}-f_{1,-1}-f_{-1,1}) \end{matrix}\right] } */ for (TIndex i = 0; i < x.rows(); i++) { for (TIndex j = 0; j < x.rows(); j++) { Scalar tmpi = xx[i]; Scalar tmpj = xx[j]; Scalar term_1 = 0; xx[i] = tmpi; xx[j] = tmpj; xx[i] += 1*eps; xx[j] += -2*eps; term_1 += value(xx); xx[i] = tmpi; xx[j] = tmpj; xx[i] += 2*eps; xx[j] += -1*eps; term_1 += value(xx); xx[i] = tmpi; xx[j] = tmpj; xx[i] += -2*eps; xx[j] += 1*eps; term_1 += value(xx); xx[i] = tmpi; xx[j] = tmpj; xx[i] += -1*eps; xx[j] += 2*eps; term_1 += value(xx); Scalar term_2 = 0; xx[i] = tmpi; xx[j] = tmpj; xx[i] += -1*eps; xx[j] += -2*eps; term_2 += value(xx); xx[i] = tmpi; xx[j] = tmpj; xx[i] += -2*eps; xx[j] += -1*eps; term_2 += value(xx); xx[i] = tmpi; xx[j] = tmpj; xx[i] += 1*eps; xx[j] += 2*eps; term_2 += value(xx); xx[i] = tmpi; xx[j] = tmpj; xx[i] += 2*eps; xx[j] += 1*eps; term_2 += value(xx); Scalar term_3 = 0; xx[i] = tmpi; xx[j] = tmpj; xx[i] += 2*eps; xx[j] += -2*eps; term_3 += value(xx); xx[i] = tmpi; xx[j] = tmpj; xx[i] += -2*eps; xx[j] += 2*eps; term_3 += value(xx); xx[i] = tmpi; xx[j] = tmpj; xx[i] += -2*eps; xx[j] += -2*eps; term_3 -= value(xx); xx[i] = tmpi; xx[j] = tmpj; xx[i] += 2*eps; xx[j] += 2*eps; term_3 -= value(xx); Scalar term_4 = 0; xx[i] = tmpi; xx[j] = tmpj; xx[i] += -1*eps; xx[j] += -1*eps; term_4 += value(xx); xx[i] = tmpi; xx[j] = tmpj; xx[i] += 1*eps; xx[j] += 1*eps; term_4 += value(xx); xx[i] = tmpi; xx[j] = tmpj; xx[i] += 1*eps; xx[j] += -1*eps; term_4 -= value(xx); xx[i] = tmpi; xx[j] = tmpj; xx[i] += -1*eps; xx[j] += 1*eps; term_4 -= value(xx); xx[i] = tmpi; xx[j] = tmpj; hessian(i, j) = (-63 * term_1+63 * term_2+44 * term_3+74 * term_4)/(600.0 * eps * eps); } } } } }; } #endif /* PROBLEM_H */ CppNumericalSolvers-1.0.0/include/cppoptlib/solver/000077500000000000000000000000001305550306500224035ustar00rootroot00000000000000CppNumericalSolvers-1.0.0/include/cppoptlib/solver/bfgssolver.h000066400000000000000000000042351305550306500247340ustar00rootroot00000000000000// CppNumericalSolver #include #include #include "isolver.h" #include "../linesearch/morethuente.h" #ifndef BFGSSOLVER_H_ #define BFGSSOLVER_H_ namespace cppoptlib { template class BfgsSolver : public ISolver { public: using Superclass = ISolver; using typename Superclass::Scalar; using typename Superclass::TVector; using typename Superclass::THessian; void minimize(ProblemType &objFunc, TVector & x0) { const size_t DIM = x0.rows(); THessian H = THessian::Identity(DIM, DIM); TVector grad(DIM); TVector x_old = x0; this->m_current.reset(); do { objFunc.gradient(x0, grad); TVector searchDir = -1 * H * grad; // check "positive definite" Scalar phi = grad.dot(searchDir); // positive definit ? if (phi > 0) { // no, we reset the hessian approximation H = THessian::Identity(DIM, DIM); searchDir = -1 * grad; } const Scalar rate = MoreThuente::linesearch(x0, searchDir, objFunc) ; x0 = x0 + rate * searchDir; TVector grad_old = grad; objFunc.gradient(x0, grad); TVector s = rate * searchDir; TVector y = grad - grad_old; const Scalar rho = 1.0 / y.dot(s); H = H - rho * (s * (y.transpose() * H) + (H * y) * s.transpose()) + rho * rho * (y.dot(H * y) + 1.0 / rho) * (s * s.transpose()); // std::cout << "iter: "<() < 1e-7 ) break; x_old = x0; ++this->m_current.iterations; this->m_current.gradNorm = grad.template lpNorm(); this->m_status = checkConvergence(this->m_stop, this->m_current); } while (objFunc.callback(this->m_current, x0) && (this->m_status == Status::Continue)); } }; } /* namespace cppoptlib */ #endif /* BFGSSOLVER_H_ */ CppNumericalSolvers-1.0.0/include/cppoptlib/solver/cmaesbsolver.h000066400000000000000000000233341305550306500252460ustar00rootroot00000000000000// CppNumericalSolver #ifndef CMAESB_H #define CMAESB_H #include #include #include "isolver.h" namespace cppoptlib { /** * @brief Covariance Matrix Adaptation */ template class CMAesBSolver : public ISolver { public: using Super = ISolver; using typename Super::Scalar; using typename Super::TVector; using typename Super::THessian; using typename Super::TCriteria; using TMatrix = Eigen::Matrix; using TVarVector = Eigen::Matrix; protected: std::mt19937 gen; Scalar m_stepSize; /* * @brief Create a vector sampled from a normal distribution * */ TVector normDist(const int n) { TVector sample = TVector::Zero(n); std::normal_distribution d(0, 1); for (int i = 0; i < n; ++i) { sample[i] = d(gen); } return sample; } std::vector index_partial_sort(const TVarVector &x, Eigen::ArrayXd::Index N) { eigen_assert(x.size() >= N); std::vector allIndices(x.size()), indices(N); for(size_t i = 0; i < allIndices.size(); i++) { allIndices[i] = i; } partial_sort(allIndices.begin(), allIndices.begin() + N, allIndices.end(), [&x](size_t i1, size_t i2) { return x[i1] < x[i2]; }); //std::cout << "SORTED: "; for (Eigen::ArrayXd::Index i = 0; i < N; i++) { indices[i] = allIndices[i]; //std::cout << indices[i] << ","; } //std::cout << std::endl; return indices; } public: CMAesBSolver() : gen((std::random_device())()) { m_stepSize = 0.5; // Set some sensible defaults for the stop criteria Super::m_stop.iterations = 1e5; Super::m_stop.gradNorm = 0; // Switch this off Super::m_stop.condition = 1e14; Super::m_stop.xDelta = 1e-7; Super::m_stop.fDelta = 1e-9; } /** * @brief minimize * @details [long description] * * @param objFunc [description] */ void minimize(TProblem &objFunc, TVector &x0) { const int n = x0.rows(); int la = ceil(4 + round(3 * log(n))); const int mu = floor(la / 2); TVarVector w = TVarVector::Zero(mu); for (int i = 0; i < mu; ++i) { w[i] = log(mu+1/2)-log(i+1); } w /= w.sum(); const Scalar mu_eff = (w.sum()*w.sum()) / w.dot(w); la = std::max(16, la); // Increase to 16 samples for very small populations, but AFTER calcaulting mu_eff const Scalar cc = (4. + mu_eff / n) / (n + 4. + 2.*mu_eff/n); const Scalar cs = (mu_eff + 2.) / (n + mu_eff + 5.); const Scalar c1 = 2. / (pow(n + 1.3, 2.) + mu_eff); const Scalar cmu = std::min(1. - c1, 2.*(mu_eff - 2. + 1./mu_eff) / (pow(n+2, 2.) + mu_eff)); const Scalar ds = 1. + cs + 2.*std::max(0., sqrt((mu_eff - 1.) / (n + 1.)) - 1.); const Scalar chi = sqrt(n) * (1. - 1./(4.*n) + 1./(21.*n*n)); const Scalar hsig_thr = (1.4 + 2 / (n + 1.)) * chi; TVector pc = TVector::Zero(n); TVector ps = TVector::Zero(n); THessian B = THessian::Identity(); THessian D = THessian::Identity(); THessian C = THessian::Zero(); C.diagonal() = (objFunc.upperBound() - objFunc.lowerBound()) / 2; Eigen::SelfAdjointEigenSolver eigenSolver(C); B = eigenSolver.eigenvectors(); D.diagonal() = eigenSolver.eigenvalues().array().sqrt(); Scalar sigma = m_stepSize; TVector xmean = x0; TVector zmean = TVector::Zero(n); TMatrix arz(n, la); TMatrix arx(n, la); TVarVector costs(la); Scalar prevCost = objFunc.value(x0); // Constraint handling TVector gamma = TVector::Ones(); // CMA-ES Main Loop int eigen_last_eval = 0; int eigen_next_eval = std::max(1, 1/(10*n*(c1+cmu))); this->m_current.reset(); if (Super::m_debug >= DebugLevel::Low) { std::cout << "CMA-ES Initial Config" << std::endl; std::cout << "n " << n << " la " << la << " mu " << mu << " mu_eff " << mu_eff << " sigma " << sigma << std::endl; std::cout << "cs " << cs << " ds " << ds << " chi " << chi << " cc " << cc << " c1 " << c1 << " cmu " << cmu << " hsig_thr " << hsig_thr << std::endl; std::cout << "C" << std::endl << C << std::endl; std::cout << "Hessian will be updated every " << eigen_next_eval << " iterations." << std::endl; std::cout << "Iteration: " << this->m_current.iterations << " best cost " << prevCost << " sigma " << sigma << " cond " << this->m_current.condition << " xmean " << x0.transpose() << std::endl; } do { for (int k = 0; k < la; ++k) { arz.col(k) = normDist(n); TVector xk = xmean + sigma * B*D*arz.col(k); arx.col(k) = xk; Scalar penalty = 0; const Scalar eta_sum = C.diagonal().array().log().sum()/n; for (int d = 0; d < n; d++) { Scalar dist = 0; const Scalar eta = exp(0.9 * (log(C.coeffRef(d, d) - eta_sum))); if (xk.coeffRef(d) < objFunc.lowerBound().coeffRef(d)) { dist = xk.coeffRef(d) - objFunc.lowerBound().coeffRef(d); xk.coeffRef(d) = objFunc.lowerBound().coeffRef(d); } else if (xk.coeffRef(d) > objFunc.upperBound().coeffRef(d)) { dist = objFunc.upperBound().coeffRef(d) - xk.coeffRef(d); xk.coeffRef(d) = objFunc.upperBound().coeffRef(d); } penalty += (dist*dist) / eta; } costs[k] = objFunc(xk) + penalty/n; } if (Super::m_debug >= DebugLevel::High) { std::cout << "arz" << std::endl << arz << std::endl; std::cout << "arx" << std::endl << arx << std::endl; std::cout << "costs " << costs.transpose() << std::endl; } std::vector indices = index_partial_sort(costs, mu); xmean = TVector::Zero(n); zmean = TVector::Zero(n); for (int k = 0; k < mu; k++) { zmean += w[k]*arz.col(indices[k]); xmean += w[k]*arx.col(indices[k]); } // Update constraint weights for (int d = 0; d < n; d++) { if (xmean.coeffRef(d) < objFunc.lowerBound().coeffRef(d) || xmean.coeffRef(d) > objFunc.upperBound().coeffRef(d)) { gamma.coeffRef(d) *= pow(1.1, std::max(1, mu_eff / (10*n))); } } // Update evolution paths ps = (1. - cs)*ps + sqrt(cs*(2. - cs)*mu_eff) * B*zmean; Scalar hsig = (ps.norm()/sqrt(pow(1 - (1. - cs), (2 * (this->m_current.iterations + 1)))) < hsig_thr) ? 1.0 : 0.0; pc = (1. - cc)*pc + hsig*sqrt(cc*(2. - cc)*mu_eff)*(B*D*zmean); // Adapt covariance matrix C = (1 - c1 - cmu)*C + c1*(pc*pc.transpose() + (1 - hsig)*cc*(2 - cc)*C); for (int k = 0; k < mu; ++k) { TVector temp = B*D*arz.col(k); C += cmu*w(k)*temp*temp.transpose(); } sigma = sigma * exp((cs/ds) * ((ps).norm()/chi - 1.)); ++Super::m_current.iterations; if ((Super::m_current.iterations - eigen_last_eval) == eigen_next_eval) { // Update B and D eigen_last_eval = Super::m_current.iterations; Eigen::SelfAdjointEigenSolver eigenSolver(C); B = eigenSolver.eigenvectors(); D.diagonal() = eigenSolver.eigenvalues().array().sqrt(); if (Super::m_debug >= DebugLevel::High) { std::cout << "Updated hessian." << std::endl; std::cout << "C" << std::endl << C << std::endl; std::cout << "B" << std::endl << B << std::endl; std::cout << "D" << std::endl << D << std::endl; } } Super::m_current.condition = D.diagonal().maxCoeff() / D.diagonal().minCoeff(); Super::m_current.xDelta = (xmean - x0).norm(); x0 = xmean; Super::m_current.fDelta = fabs(costs[indices[0]] - prevCost); prevCost = costs[indices[0]]; if (Super::m_debug >= DebugLevel::Low) { std::cout << "Iteration: " << this->m_current.iterations << " best cost " << costs[indices[0]] << " sigma " << sigma << " cond " << this->m_current.condition << " xmean " << xmean.transpose() << std::endl; } if (fabs(costs[indices[0]] - costs[indices[mu-1]]) < 1e-6) { sigma = sigma * exp(0.2+cs/ds); if (Super::m_debug >= DebugLevel::Low) { std::cout << "Flat fitness " << costs[indices[0]] << " " << costs[indices[mu-1]] << std::endl; } } Super::m_status = checkConvergence(this->m_stop, this->m_current); } while (objFunc.callback(this->m_current, x0) && (this->m_status == Status::Continue)); // Return the best evaluated solution x0 = xmean; m_stepSize = sigma; if (Super::m_debug >= DebugLevel::Low) { std::cout << "Stop" << std::endl; this->m_stop.print(std::cout); std::cout << "Current" << std::endl; this->m_current.print(std::cout); std::cout << "Reason: " << Super::m_status << std::endl; } } }; } /* namespace cppoptlib */ #endif /* CMAES_H_ */ CppNumericalSolvers-1.0.0/include/cppoptlib/solver/cmaessolver.h000066400000000000000000000207301305550306500251010ustar00rootroot00000000000000// CppNumericalSolver #ifndef CMAES_H_ #define CMAES_H_ #include #include #include "isolver.h" namespace cppoptlib { /** * @brief Covariance Matrix Adaptation */ template class CMAesSolver : public ISolver { public: using Super = ISolver; using typename Super::Scalar; using typename Super::TVector; using typename Super::THessian; using typename Super::TCriteria; using TMatrix = Eigen::Matrix; using TVarVector = Eigen::Matrix; protected: std::mt19937 gen; Scalar m_stepSize; /* * @brief Create a vector sampled from a normal distribution * */ TVector normDist(const int n) { TVector sample = TVector::Zero(n); std::normal_distribution d(0, 1); for (int i = 0; i < n; ++i) { sample[i] = d(gen); } return sample; } std::vector index_partial_sort(const TVarVector &x, Eigen::ArrayXd::Index N) { eigen_assert(x.size() >= N); std::vector allIndices(x.size()), indices(N); for(size_t i = 0; i < allIndices.size(); i++) { allIndices[i] = i; } partial_sort(allIndices.begin(), allIndices.begin() + N, allIndices.end(), [&x](size_t i1, size_t i2) { return x[i1] < x[i2]; }); //std::cout << "SORTED: "; for (Eigen::ArrayXd::Index i = 0; i < N; i++) { indices[i] = allIndices[i]; //std::cout << indices[i] << ","; } //std::cout << std::endl; return indices; } public: CMAesSolver() : gen((std::random_device())()) { m_stepSize = 0.5; // Set some sensible defaults for the stop criteria Super::m_stop.iterations = 1e5; Super::m_stop.gradNorm = 0; // Switch this off Super::m_stop.condition = 1e14; Super::m_stop.xDelta = 1e-7; Super::m_stop.fDelta = 1e-9; } void minimize(TProblem &objFunc, TVector &x0) { TVector var0 = TVector::Ones(x0.rows()); this->minimize(objFunc, x0, var0); } /** * @brief minimize * @details [long description] * * @param objFunc [description] */ void minimize(TProblem &objFunc, TVector &x0, const TVector &var0) { const int n = x0.rows(); eigen_assert(x0.rows() == var0.rows()); int la = ceil(4 + round(3 * log(n))); const int mu = floor(la / 2); TVarVector w = TVarVector::Zero(mu); for (int i = 0; i < mu; ++i) { w[i] = log(mu+1/2)-log(i+1); } w /= w.sum(); const Scalar mu_eff = (w.sum()*w.sum()) / w.dot(w); la = std::max(16, la); // Increase to 16 samples for very small populations, but AFTER calcaulting mu_eff const Scalar cc = (4. + mu_eff / n) / (n + 4. + 2.*mu_eff/n); const Scalar cs = (mu_eff + 2.) / (n + mu_eff + 5.); const Scalar c1 = 2. / (pow(n + 1.3, 2.) + mu_eff); const Scalar cmu = std::min(1. - c1, 2.*(mu_eff - 2. + 1./mu_eff) / (pow(n+2, 2.) + mu_eff)); const Scalar ds = 1. + cs + 2.*std::max(0., sqrt((mu_eff - 1.) / (n + 1.)) - 1.); const Scalar chi = sqrt(n) * (1. - 1./(4.*n) + 1./(21.*n*n)); const Scalar hsig_thr = (1.4 + 2 / (n + 1.)) * chi; TVector pc = TVector::Zero(n); TVector ps = TVector::Zero(n); THessian B = THessian::Identity(); THessian D = THessian::Identity(); THessian C = B*D*(B*D).transpose(); Scalar sigma = m_stepSize; TVector xmean = x0; TVector zmean = TVector::Zero(n); TMatrix arz(n, la); TMatrix arx(n, la); TVarVector costs(la); Scalar prevCost = objFunc.value(x0); // CMA-ES Main Loop int eigen_last_eval = 0; int eigen_next_eval = std::max(1, 1/(10*n*(c1+cmu))); this->m_current.reset(); if (Super::m_debug >= DebugLevel::Low) { std::cout << "CMA-ES Initial Config" << std::endl; std::cout << "n " << n << " la " << la << " mu " << mu << " mu_eff " << mu_eff << " sigma " << sigma << std::endl; std::cout << "cs " << cs << " ds " << ds << " chi " << chi << " cc " << cc << " c1 " << c1 << " cmu " << cmu << " hsig_thr " << hsig_thr << std::endl; std::cout << "C" << std::endl << C << std::endl; std::cout << "Hessian will be updated every " << eigen_next_eval << " iterations." << std::endl; std::cout << "Iteration: " << this->m_current.iterations << " best cost " << prevCost << " sigma " << sigma << " cond " << this->m_current.condition << " xmean " << x0.transpose() << std::endl; } do { for (int k = 0; k < la; ++k) { arz.col(k) = normDist(n); arx.col(k) = xmean + sigma * B*D*arz.col(k); costs[k] = objFunc(arx.col(k)); } if (Super::m_debug >= DebugLevel::High) { std::cout << "arz" << std::endl << arz << std::endl; std::cout << "arx" << std::endl << arx << std::endl; std::cout << "costs " << costs.transpose() << std::endl; } std::vector indices = index_partial_sort(costs, mu); xmean = TVector::Zero(n); zmean = TVector::Zero(n); for (int k = 0; k < mu; k++) { zmean += w[k]*arz.col(indices[k]); xmean += w[k]*arx.col(indices[k]); } // Update evolution paths ps = (1. - cs)*ps + sqrt(cs*(2. - cs)*mu_eff) * B*zmean; Scalar hsig = (ps.norm()/sqrt(pow(1 - (1. - cs), (2 * (this->m_current.iterations + 1)))) < hsig_thr) ? 1.0 : 0.0; pc = (1. - cc)*pc + hsig*sqrt(cc*(2. - cc)*mu_eff)*(B*D*zmean); // Adapt covariance matrix C = (1 - c1 - cmu)*C + c1*(pc*pc.transpose() + (1 - hsig)*cc*(2 - cc)*C); for (int k = 0; k < mu; ++k) { TVector temp = B*D*arz.col(k); C += cmu*w(k)*temp*temp.transpose(); } sigma = sigma * exp((cs/ds) * ((ps).norm()/chi - 1.)); ++Super::m_current.iterations; if ((Super::m_current.iterations - eigen_last_eval) == eigen_next_eval) { // Update B and D eigen_last_eval = Super::m_current.iterations; Eigen::SelfAdjointEigenSolver eigenSolver(C); B = eigenSolver.eigenvectors(); D.diagonal() = eigenSolver.eigenvalues().array().sqrt(); if (Super::m_debug >= DebugLevel::High) { std::cout << "Updated hessian." << std::endl; std::cout << "C" << std::endl << C << std::endl; std::cout << "B" << std::endl << B << std::endl; std::cout << "D" << std::endl << D << std::endl; } } Super::m_current.condition = D.diagonal().maxCoeff() / D.diagonal().minCoeff(); Super::m_current.xDelta = (xmean - x0).norm(); x0 = xmean; Super::m_current.fDelta = fabs(costs[indices[0]] - prevCost); prevCost = costs[indices[0]]; if (Super::m_debug >= DebugLevel::Low) { std::cout << "Iteration: " << this->m_current.iterations << " best cost " << costs[indices[0]] << " sigma " << sigma << " cond " << this->m_current.condition << " xmean " << xmean.transpose() << std::endl; } if (fabs(costs[indices[0]] - costs[indices[mu-1]]) < 1e-6) { sigma = sigma * exp(0.2+cs/ds); if (Super::m_debug >= DebugLevel::Low) { std::cout << "Flat fitness " << costs[indices[0]] << " " << costs[indices[mu-1]] << std::endl; } } Super::m_status = checkConvergence(this->m_stop, this->m_current); } while (objFunc.callback(this->m_current, x0) && (this->m_status == Status::Continue)); // Return the best evaluated solution x0 = xmean; m_stepSize = sigma; if (Super::m_debug >= DebugLevel::Low) { std::cout << "Stop" << std::endl; this->m_stop.print(std::cout); std::cout << "Current" << std::endl; this->m_current.print(std::cout); std::cout << "Reason: " << Super::m_status << std::endl; } } }; } /* namespace cppoptlib */ #endif /* CMAES_H_ */ CppNumericalSolvers-1.0.0/include/cppoptlib/solver/conjugatedgradientdescentsolver.h000066400000000000000000000031251305550306500312170ustar00rootroot00000000000000// CppNumericalSolver #ifndef CONJUGATEDGRADIENTDESCENTSOLVER_H_ #define CONJUGATEDGRADIENTDESCENTSOLVER_H_ #include #include "isolver.h" #include "../linesearch/armijo.h" namespace cppoptlib { template class ConjugatedGradientDescentSolver : public ISolver { public: using Superclass = ISolver; using typename Superclass::Scalar; using typename Superclass::TVector; /** * @brief minimize * @details [long description] * * @param objFunc [description] */ void minimize(ProblemType &objFunc, TVector &x0) { TVector grad(x0.rows()); TVector grad_old(x0.rows()); TVector Si(x0.rows()); TVector Si_old(x0.rows()); this->m_current.reset(); do { objFunc.gradient(x0, grad); if (this->m_current.iterations == 0) { Si = -grad; } else { const double beta = grad.dot(grad) / (grad_old.dot(grad_old)); Si = -grad + beta * Si_old; } const double rate = Armijo::linesearch(x0, Si, objFunc) ; x0 = x0 + rate * Si; grad_old = grad; Si_old = Si; this->m_current.gradNorm = grad.template lpNorm(); // std::cout << "iter: "<m_current.iterations; this->m_status = checkConvergence(this->m_stop, this->m_current); } while (objFunc.callback(this->m_current, x0) && (this->m_status == Status::Continue) ); } }; } /* namespace cppoptlib */ #endif /* CONJUGATEDGRADIENTDESCENTSOLVER_H_ */ CppNumericalSolvers-1.0.0/include/cppoptlib/solver/gradientdescentsolver.h000066400000000000000000000030701305550306500271520ustar00rootroot00000000000000// CppNumericalSolver #ifndef GRADIENTDESCENTSOLVER_H_ #define GRADIENTDESCENTSOLVER_H_ #include #include "isolver.h" #include "../linesearch/morethuente.h" namespace cppoptlib { template class GradientDescentSolver : public ISolver { public: using Superclass = ISolver; using typename Superclass::Scalar; using typename Superclass::TVector; /** * @brief minimize * @details [long description] * * @param objFunc [description] */ void minimize(ProblemType &objFunc, TVector &x0) { TVector direction(x0.rows()); this->m_current.reset(); do { ; objFunc.gradient(x0, direction); const Scalar rate = MoreThuente::linesearch(x0, -direction, objFunc) ; x0 = x0 - rate * direction; this->m_current.gradNorm = direction.template lpNorm(); // std::cout << "iter: "<m_current.iterations; this->m_status = checkConvergence(this->m_stop, this->m_current); } while (objFunc.callback(this->m_current, x0) && (this->m_status == Status::Continue)); if (this->m_debug > DebugLevel::None) { std::cout << "Stop status was: " << this->m_status << std::endl; std::cout << "Stop criteria were: " << std::endl << this->m_stop << std::endl; std::cout << "Current values are: " << std::endl << this->m_current << std::endl; } } }; } /* namespace cppoptlib */ #endif /* GRADIENTDESCENTSOLVER_H_ */ CppNumericalSolvers-1.0.0/include/cppoptlib/solver/isolver.h000066400000000000000000000027331305550306500242440ustar00rootroot00000000000000// CppNumericalSolver #ifndef ISOLVER_H_ #define ISOLVER_H_ #include #include "isolver.h" #include "../meta.h" #include "../problem.h" namespace cppoptlib { template class ISolver { public: using Scalar = typename ProblemType::Scalar; using TVector = typename ProblemType::TVector; using THessian = typename ProblemType::THessian; using TCriteria = typename ProblemType::TCriteria; protected: const int order_ = Ord; TCriteria m_stop, m_current; Status m_status = Status::NotStarted; DebugLevel m_debug = DebugLevel::None; public: virtual ~ISolver() = default; ISolver() { m_stop = TCriteria::defaults(); m_current.reset(); } ISolver(const TCriteria &s) { m_stop = s; m_current.reset(); } void setStopCriteria(const TCriteria &s) { m_stop = s; } const TCriteria &criteria() { return m_current; } const Status &status() { return m_status; } void setDebug(const DebugLevel &d) { m_debug = d; } /** * @brief minimize an objective function given a gradient (and optinal a hessian) * @details this is just the abstract interface * * @param x0 starting point * @param funObjective objective function * @param funGradient gradient function * @param funcHession hessian function */ virtual void minimize(ProblemType &objFunc, TVector &x0) = 0; }; } /* namespace cppoptlib */ #endif /* ISOLVER_H_ */ CppNumericalSolvers-1.0.0/include/cppoptlib/solver/lbfgsbsolver.h000066400000000000000000000252561305550306500252600ustar00rootroot00000000000000// CppNumericalSolver #include #include #include #include "isolver.h" #include "../boundedproblem.h" #include "../linesearch/morethuente.h" #ifndef LBFGSBSOLVER_H #define LBFGSBSOLVER_H namespace cppoptlib { template class LbfgsbSolver : public ISolver { public: using Superclass = ISolver; using typename Superclass::Scalar; using typename Superclass::TVector; using MatrixType = Eigen::Matrix; using VariableTVector = Eigen::Matrix; protected: // workspace matrices MatrixType W, M; Scalar theta; int DIM; int m_historySize = 5; /** * @brief sort pairs (k,v) according v ascending * @details [long description] * * @param v [description] * @return [description] */ std::vector sort_indexes(const std::vector< std::pair > &v) { std::vector idx(v.size()); for (size_t i = 0; i != idx.size(); ++i) idx[i] = v[i].first; sort(idx.begin(), idx.end(), [&v](size_t i1, size_t i2) { return v[i1].second < v[i2].second; }); return idx; } /** * @brief Algorithm CP: Computation of the generalized Cauchy point * @details PAGE 8 * * @param c [description] */ void getGeneralizedCauchyPoint(const TProblem &problem, const TVector &x, const TVector &g, TVector &x_cauchy, VariableTVector &c) { const int DIM = x.rows(); // Given x,l,u,g, and B = \theta I-WMW // {all t_i} = { (idx,value), ... } // TODO: use "std::set" ? std::vector > SetOfT; // the feasible set is implicitly given by "SetOfT - {t_i==0}" TVector d = -g; // n operations for (int j = 0; j < DIM; j++) { if (g(j) == 0) { SetOfT.push_back(std::make_pair(j, std::numeric_limits::max())); } else { Scalar tmp = 0; if (g(j) < 0) { tmp = (x(j) - problem.upperBound()(j)) / g(j); } else { tmp = (x(j) - problem.lowerBound()(j)) / g(j); } SetOfT.push_back(std::make_pair(j, tmp)); if (tmp == 0) d(j) = 0; } } // sortedindices [1,0,2] means the minimal element is on the 1-st entry std::vector sortedIndices = sort_indexes(SetOfT); x_cauchy = x; // Initialize // p := W^Scalar*p VariableTVector p = (W.transpose() * d); // (2mn operations) // c := 0 c = VariableTVector::Zero(W.cols()); // f' := g^Scalar*d = -d^Td Scalar f_prime = -d.dot(d); // (n operations) // f'' := \theta*d^Scalar*d-d^Scalar*W*M*W^Scalar*d = -\theta*f' - p^Scalar*M*p Scalar f_doubleprime = (Scalar)(-1.0 * theta) * f_prime - p.dot(M * p); // (O(m^2) operations) f_doubleprime = std::max(std::numeric_limits::epsilon(), f_doubleprime); Scalar f_dp_orig = f_doubleprime; // \delta t_min := -f'/f'' Scalar dt_min = -f_prime / f_doubleprime; // t_old := 0 Scalar t_old = 0; // b := argmin {t_i , t_i >0} int i = 0; for (int j = 0; j < DIM; j++) { i = j; if (SetOfT[sortedIndices[j]].second > 0) break; } int b = sortedIndices[i]; // see below // t := min{t_i : i in F} Scalar t = SetOfT[b].second; // \delta Scalar := t - 0 Scalar dt = t ; // examination of subsequent segments while ((dt_min >= dt) && (i < DIM)) { if (d(b) > 0) x_cauchy(b) = problem.upperBound()(b); else if (d(b) < 0) x_cauchy(b) = problem.lowerBound()(b); // z_b = x_p^{cp} - x_b Scalar zb = x_cauchy(b) - x(b); // c := c +\delta t*p c += dt * p; // cache VariableTVector wbt = W.row(b); f_prime += dt * f_doubleprime + (Scalar) g(b) * g(b) + (Scalar) theta * g(b) * zb - (Scalar) g(b) * wbt.transpose() * (M * c); f_doubleprime += (Scalar) - 1.0 * theta * g(b) * g(b) - (Scalar) 2.0 * (g(b) * (wbt.dot(M * p))) - (Scalar) g(b) * g(b) * wbt.transpose() * (M * wbt); f_doubleprime = std::max(std::numeric_limits::epsilon() * f_dp_orig, f_doubleprime); p += g(b) * wbt.transpose(); d(b) = 0; dt_min = -f_prime / f_doubleprime; t_old = t; ++i; if (i < DIM) { b = sortedIndices[i]; t = SetOfT[b].second; dt = t - t_old; } } dt_min = std::max(dt_min, (Scalar)0.0); t_old += dt_min; #pragma omp parallel for for (int ii = i; ii < x_cauchy.rows(); ii++) { x_cauchy(sortedIndices[ii]) = x(sortedIndices[ii]) + t_old * d(sortedIndices[ii]); } c += dt_min * p; } /** * @brief find alpha* = max {a : a <= 1 and l_i-xc_i <= a*d_i <= u_i-xc_i} * @details [long description] * * @param FreeVariables [description] * @return [description] */ Scalar findAlpha(const TProblem &problem, TVector &x_cp, VariableTVector &du, std::vector &FreeVariables) { Scalar alphastar = 1; const unsigned int n = FreeVariables.size(); assert(du.rows() == n); for (unsigned int i = 0; i < n; i++) { if (du(i) > 0) { alphastar = std::min(alphastar, (problem.upperBound()(FreeVariables[i]) - x_cp(FreeVariables[i])) / du(i)); } else { alphastar = std::min(alphastar, (problem.lowerBound()(FreeVariables[i]) - x_cp(FreeVariables[i])) / du(i)); } } return alphastar; } /** * @brief solving unbounded probelm * @details [long description] * * @param SubspaceMin [description] */ void SubspaceMinimization(const TProblem &problem, TVector &x_cauchy, TVector &x, VariableTVector &c, TVector &g, TVector &SubspaceMin) { Scalar theta_inverse = 1 / theta; std::vector FreeVariablesIndex; for (int i = 0; i < x_cauchy.rows(); i++) { if ((x_cauchy(i) != problem.upperBound()(i)) && (x_cauchy(i) != problem.lowerBound()(i))) { FreeVariablesIndex.push_back(i); } } const int FreeVarCount = FreeVariablesIndex.size(); MatrixType WZ = MatrixType::Zero(W.cols(), FreeVarCount); for (int i = 0; i < FreeVarCount; i++) WZ.col(i) = W.row(FreeVariablesIndex[i]); TVector rr = (g + theta * (x_cauchy - x) - W * (M * c)); // r=r(FreeVariables); MatrixType r = MatrixType::Zero(FreeVarCount, 1); for (int i = 0; i < FreeVarCount; i++) r.row(i) = rr.row(FreeVariablesIndex[i]); // STEP 2: "v = w^T*Z*r" and STEP 3: "v = M*v" VariableTVector v = M * (WZ * r); // STEP 4: N = 1/theta*W^T*Z*(W^T*Z)^T MatrixType N = theta_inverse * WZ * WZ.transpose(); // N = I - MN N = MatrixType::Identity(N.rows(), N.rows()) - M * N; // STEP: 5 // v = N^{-1}*v v = N.lu().solve(v); // STEP: 6 // HERE IS A MISTAKE IN THE ORIGINAL PAPER! VariableTVector du = -theta_inverse * r - theta_inverse * theta_inverse * WZ.transpose() * v; // STEP: 7 Scalar alpha_star = findAlpha(problem, x_cauchy, du, FreeVariablesIndex); // STEP: 8 VariableTVector dStar = alpha_star * du; SubspaceMin = x_cauchy; for (int i = 0; i < FreeVarCount; i++) { SubspaceMin(FreeVariablesIndex[i]) = SubspaceMin(FreeVariablesIndex[i]) + dStar(i); } } public: void setHistorySize(const int hs) { m_historySize = hs; } void minimize(TProblem &problem, TVector &x0) { DIM = x0.rows(); theta = 1.0; W = MatrixType::Zero(DIM, 0); M = MatrixType::Zero(0, 0); MatrixType yHistory = MatrixType::Zero(DIM, 0); MatrixType sHistory = MatrixType::Zero(DIM, 0); TVector x = x0, g = x0; Scalar f = problem.value(x); problem.gradient(x, g); // conv. crit. auto noConvergence = [&](TVector &x, TVector &g)->bool { return (((x - g).cwiseMax(problem.lowerBound()).cwiseMin(problem.upperBound()) - x).template lpNorm() >= 1e-4); }; this->m_current.reset(); this->m_status = Status::Continue; while (problem.callback(this->m_current, x) && noConvergence(x, g) && (this->m_status == Status::Continue)) { Scalar f_old = f; TVector x_old = x; TVector g_old = g; // STEP 2: compute the cauchy point TVector CauchyPoint = TVector::Zero(DIM); VariableTVector c = VariableTVector::Zero(W.cols()); getGeneralizedCauchyPoint(problem, x, g, CauchyPoint, c); // STEP 3: compute a search direction d_k by the primal method for the sub-problem TVector SubspaceMin; SubspaceMinimization(problem, CauchyPoint, x, c, g, SubspaceMin); // STEP 4: perform linesearch and STEP 5: compute gradient Scalar alpha_init = 1.0; const Scalar rate = MoreThuente::linesearch(x, SubspaceMin-x , problem, alpha_init); // update current guess and function information x = x - rate*(x-SubspaceMin); f = problem.value(x); problem.gradient(x, g); // prepare for next iteration TVector newY = g - g_old; TVector newS = x - x_old; // STEP 6: Scalar test = newS.dot(newY); test = (test < 0) ? -1.0 * test : test; if (test > 1e-7 * newY.squaredNorm()) { if (yHistory.cols() < m_historySize) { yHistory.conservativeResize(DIM, yHistory.cols() + 1); sHistory.conservativeResize(DIM, sHistory.cols() + 1); } else { yHistory.leftCols(m_historySize - 1) = yHistory.rightCols(m_historySize - 1).eval(); sHistory.leftCols(m_historySize - 1) = sHistory.rightCols(m_historySize - 1).eval(); } yHistory.rightCols(1) = newY; sHistory.rightCols(1) = newS; // STEP 7: theta = (Scalar)(newY.transpose() * newY) / (newY.transpose() * newS); W = MatrixType::Zero(yHistory.rows(), yHistory.cols() + sHistory.cols()); W << yHistory, (theta * sHistory); MatrixType A = sHistory.transpose() * yHistory; MatrixType L = A.template triangularView(); MatrixType MM(A.rows() + L.rows(), A.rows() + L.cols()); MatrixType D = -1 * A.diagonal().asDiagonal(); MM << D, L.transpose(), L, ((sHistory.transpose() * sHistory) * theta); M = MM.inverse(); } if (fabs(f_old - f) < 1e-8) { // successive function values too similar break; } ++this->m_current.iterations; this->m_current.gradNorm = g.norm(); this->m_status = checkConvergence(this->m_stop, this->m_current); } x0 = x; if (this->m_debug > DebugLevel::None) { std::cout << "Stop status was: " << this->m_status << std::endl; std::cout << "Stop criteria were: " << std::endl << this->m_stop << std::endl; std::cout << "Current values are: " << std::endl << this->m_current << std::endl; } } }; } /* namespace cppoptlib */ #endif /* LBFGSBSOLVER_H_ */ CppNumericalSolvers-1.0.0/include/cppoptlib/solver/lbfgssolver.h000066400000000000000000000102341305550306500251040ustar00rootroot00000000000000// CppNumericalSolver #include #include #include "isolver.h" #include "../linesearch/morethuente.h" #ifndef LBFGSSOLVER_H_ #define LBFGSSOLVER_H_ namespace cppoptlib { template class LbfgsSolver : public ISolver { public: using Superclass = ISolver; using typename Superclass::Scalar; using typename Superclass::TVector; using typename Superclass::THessian; using MatrixType = Eigen::Matrix; void minimize(ProblemType &objFunc, TVector &x0) { const size_t m = 10; const size_t DIM = x0.rows(); MatrixType sVector = MatrixType::Zero(DIM, m); MatrixType yVector = MatrixType::Zero(DIM, m); Eigen::Matrix alpha = Eigen::Matrix::Zero(m); TVector grad(DIM), q(DIM), grad_old(DIM), s(DIM), y(DIM); objFunc.gradient(x0, grad); TVector x_old = x0; TVector x_old2 = x0; size_t iter = 0, globIter = 0; Scalar H0k = 1; this->m_current.reset(); do { const Scalar relativeEpsilon = static_cast(0.0001) * std::max(static_cast(1.0), x0.norm()); if (grad.norm() < relativeEpsilon) break; //Algorithm 7.4 (L-BFGS two-loop recursion) q = grad; const int k = std::min(m, iter); // for i = k − 1, k − 2, . . . , k − m§ for (int i = k - 1; i >= 0; i--) { // alpha_i <- rho_i*s_i^T*q const double rho = 1.0 / static_cast(sVector.col(i)) .dot(static_cast(yVector.col(i))); alpha(i) = rho * static_cast(sVector.col(i)).dot(q); // q <- q - alpha_i*y_i q = q - alpha(i) * yVector.col(i); } // r <- H_k^0*q q = H0k * q; //for i k − m, k − m + 1, . . . , k − 1 for (int i = 0; i < k; i++) { // beta <- rho_i * y_i^T * r const Scalar rho = 1.0 / static_cast(sVector.col(i)) .dot(static_cast(yVector.col(i))); const Scalar beta = rho * static_cast(yVector.col(i)).dot(q); // r <- r + s_i * ( alpha_i - beta) q = q + sVector.col(i) * (alpha(i) - beta); } // stop with result "H_k*f_f'=q" // any issues with the descent direction ? Scalar descent = -grad.dot(q); Scalar alpha_init = 1.0 / grad.norm(); if (descent > -0.0001 * relativeEpsilon) { q = -1 * grad; iter = 0; alpha_init = 1.0; } // find steplength const Scalar rate = MoreThuente::linesearch(x0, -q, objFunc, alpha_init) ; // update guess x0 = x0 - rate * q; grad_old = grad; objFunc.gradient(x0, grad); s = x0 - x_old; y = grad - grad_old; // update the history if (iter < m) { sVector.col(iter) = s; yVector.col(iter) = y; } else { sVector.leftCols(m - 1) = sVector.rightCols(m - 1).eval(); sVector.rightCols(1) = s; yVector.leftCols(m - 1) = yVector.rightCols(m - 1).eval(); yVector.rightCols(1) = y; } // update the scaling factor H0k = y.dot(s) / static_cast(y.dot(y)); x_old = x0; // std::cout << "iter: "<m_current.iterations; this->m_current.gradNorm = grad.template lpNorm(); this->m_status = checkConvergence(this->m_stop, this->m_current); } while ((objFunc.callback(this->m_current, x0)) && (this->m_status == Status::Continue)); } }; } /* namespace cppoptlib */ #endif /* LBFGSSOLVER_H_ */ CppNumericalSolvers-1.0.0/include/cppoptlib/solver/neldermeadsolver.h000066400000000000000000000160061305550306500261120ustar00rootroot00000000000000// CppNumericalSolver #ifndef NELDERMEADSOLVER_H_ #define NELDERMEADSOLVER_H_ #include #include #include "isolver.h" #include "../meta.h" namespace cppoptlib { template class NelderMeadSolver : public ISolver { public: using Superclass = ISolver; using typename Superclass::Scalar; using typename Superclass::TVector; using MatrixType = Eigen::Matrix; MatrixType x0; SimplexOp lastOp = SimplexOp::Place; Status stop_condition; bool initialSimplexCreated = false; MatrixType makeInitialSimplex(TVector &x) { size_t DIM = x.rows(); MatrixType s = MatrixType::Zero(DIM, DIM + 1); for (int c = 0; c < int(DIM) + 1; ++c) { for (int r = 0; r < int(DIM); ++r) { s(r, c) = x(r); if (r == c - 1) { if (x(r) == 0) { s(r, c) = 0.00025; } s(r, c) = (1 + 0.05) * x(r); } } } return s; } /** * @brief minimize * @details [long description] * * @param objFunc [description] */ void minimize(ProblemType &objFunc, TVector &x) { const Scalar rho = 1.; // rho > 0 const Scalar xi = 2.; // xi > max(rho, 1) const Scalar gam = 0.5; // 0 < gam < 1 const size_t DIM = x.rows(); // create initial simplex if (not initialSimplexCreated) { x0 = makeInitialSimplex(x); } // compute function values std::vector f; f.resize(DIM + 1); std::vector index; index.resize(DIM + 1); for (int i = 0; i < int(DIM) + 1; ++i) { f[i] = objFunc(static_cast(x0.col(i))); index[i] = i; } sort(index.begin(), index.end(), [&](int a, int b)-> bool { return f[a] < f[b]; }); int iter = 0; const int maxIter = this->m_stop.iterations * DIM; while ( objFunc.callback(this->m_current, x0.col(index[0])) and (iter < maxIter) ) { // conv-check Scalar max1 = fabs(f[index[1]] - f[index[0]]); Scalar max2 = (x0.col(index[1]) - x0.col(index[0]) ).array().abs().maxCoeff(); for (int i = 2; i < int(DIM) + 1; ++i) { Scalar tmp1 = fabs(f[index[i]] - f[index[0]]); if (tmp1 > max1) max1 = tmp1; Scalar tmp2 = (x0.col(index[i]) - x0.col(index[0]) ).array().abs().maxCoeff(); if (tmp2 > max2) max2 = tmp2; } const Scalar tt1 = std::max(Scalar(1.e-04), 10 * std::nextafter(f[index[0]], std::numeric_limits::epsilon()) - f[index[0]]); const Scalar tt2 = std::max(Scalar(1.e-04), 10 * (std::nextafter(x0.col(index[0]).maxCoeff(), std::numeric_limits::epsilon()) - x0.col(index[0]).maxCoeff())); // User-defined stopping criteria this->m_current.iterations = iter; this->m_current.fDelta = max1; this->m_current.xDelta = max2; stop_condition = checkConvergence(this->m_stop, this->m_current); if (this->m_stop.iterations != 0 and stop_condition != Status::Continue) { break; } // Allow stopping in the callback. This callback gets the correct current // state unlike the simple one in while(), which get previous state. if (objFunc.detailed_callback(this->m_current, lastOp, index[0], x0, f) == false) { stop_condition = Status::UserDefined; break; } // max(||x - shift(x) ||_inf ) <= tol, if (max1 <= tt1) { // values to similar if (max2 <= tt2) { stop_condition = Status::FDeltaTolerance; break; } } ////////////////////////// // midpoint of the simplex opposite the worst point TVector x_bar = TVector::Zero(DIM); for (int i = 0; i < int(DIM); ++i) { x_bar += x0.col(index[i]); } x_bar /= Scalar(DIM); // Compute the reflection point const TVector x_r = ( 1. + rho ) * x_bar - rho * x0.col(index[DIM]); const Scalar f_r = objFunc(x_r); lastOp = SimplexOp::Reflect; if (f_r < f[index[0]]) { // the expansion point const TVector x_e = ( 1. + rho * xi ) * x_bar - rho * xi * x0.col(index[DIM]); const Scalar f_e = objFunc(x_e); if ( f_e < f_r ) { // expand lastOp = SimplexOp::Expand; x0.col(index[DIM]) = x_e; f[index[DIM]] = f_e; } else { // reflect lastOp = SimplexOp::Reflect; x0.col(index[DIM]) = x_r; f[index[DIM]] = f_r; } } else { if ( f_r < f[index[DIM]] ) { x0.col(index[DIM]) = x_r; f[index[DIM]] = f_r; } else { // contraction if (f_r < f[index[DIM]]) { const TVector x_c = (1 + rho * gam) * x_bar - rho * gam * x0.col(index[DIM]); const Scalar f_c = objFunc(x_c); if ( f_c <= f_r ) { // outside x0.col(index[DIM]) = x_c; f[index[DIM]] = f_c; lastOp = SimplexOp::ContractOut; } else { shrink(x0, index, f, objFunc); lastOp = SimplexOp::Shrink; } } else { // inside const TVector x_c = ( 1 - gam ) * x_bar + gam * x0.col(index[DIM]); const Scalar f_c = objFunc(x_c); if (f_c < f[index[DIM]]) { x0.col(index[DIM]) = x_c; f[index[DIM]] = f_c; lastOp = SimplexOp::ContractIn; } else { shrink(x0, index, f, objFunc); lastOp = SimplexOp::Shrink; } } } } sort(index.begin(), index.end(), [&](int a, int b)-> bool { return f[a] < f[b]; }); iter++; if (iter >= maxIter) { stop_condition = Status::IterationLimit; } else { stop_condition = Status::UserDefined; // if stopped in the callback in while() } } // while loop // report the last result objFunc.detailed_callback(this->m_current, lastOp, index[0], x0, f); x = x0.col(index[0]); } void shrink(MatrixType &x, std::vector &index, std::vector &f, ProblemType &objFunc) { const Scalar sig = 0.5; // 0 < sig < 1 const int DIM = x.rows(); f[index[0]] = objFunc(x.col(index[0])); for (int i = 1; i < DIM + 1; ++i) { x.col(index[i]) = sig * x.col(index[i]) + (1. - sig) * x.col(index[0]); f[index[i]] = objFunc(x.col(index[i])); } } // Need our own checker here to get rid of the gradient test used in other solvers template Status checkConvergence(const Criteria &stop, const Criteria ¤t) { if ((stop.iterations > 0) && (current.iterations > stop.iterations)) { return Status::IterationLimit; } if ((stop.xDelta > 0) && (current.xDelta < stop.xDelta)) { return Status::XDeltaTolerance; } if ((stop.fDelta > 0) && (current.fDelta < stop.fDelta)) { return Status::FDeltaTolerance; } return Status::Continue; } }; /* class NelderMeadSolver */ } /* namespace cppoptlib */ #endif /* NELDERMEADSOLVER_H_ */ CppNumericalSolvers-1.0.0/include/cppoptlib/solver/newtondescentsolver.h000066400000000000000000000030251305550306500266670ustar00rootroot00000000000000// CppNumericalSolver #include #include #include "isolver.h" #include "../linesearch/armijo.h" #ifndef NEWTONDESCENTSOLVER_H_ #define NEWTONDESCENTSOLVER_H_ namespace cppoptlib { template class NewtonDescentSolver : public ISolver { public: using Superclass = ISolver; using typename Superclass::Scalar; using typename Superclass::TVector; using typename Superclass::THessian; void minimize(ProblemType &objFunc, TVector &x0) { const int DIM = x0.rows(); TVector grad = TVector::Zero(DIM); THessian hessian = THessian::Zero(DIM, DIM); Scalar gradNorm = 0; this->m_current.reset(); do { objFunc.gradient(x0, grad); objFunc.hessian(x0, hessian); hessian += (1e-5) * THessian::Identity(DIM, DIM); TVector delta_x = hessian.lu().solve(-grad); const double rate = Armijo::linesearch(x0, delta_x, objFunc) ; x0 = x0 + rate * delta_x; // std::cout << "iter: "<m_current.iterations; this->m_current.gradNorm = grad.template lpNorm(); this->m_status = checkConvergence(this->m_stop, this->m_current); } while (objFunc.callback(this->m_current, x0) && (this->m_status == Status::Continue)); } }; } /* namespace cppoptlib */ #endif /* NEWTONDESCENTSOLVER_H_ */ CppNumericalSolvers-1.0.0/include/cppoptlib/timer.h000066400000000000000000000032771305550306500223730ustar00rootroot00000000000000#ifndef TIMER_H #define TIMER_H #include #include namespace cppoptlib { template class timer { std::chrono::time_point start_v; std::chrono::time_point pause_v; std::chrono::time_point end_v; bool stopped; bool paused; public: explicit timer() : stopped(false), paused(false) { start_v = C::now(); } ~timer() {} /** * @brief start stopwatch * @details [long description] */ void start() { start_v = C::now(); paused = false; stopped = false; } /** * @brief break stopwatch, but allows resuming * @details [long description] */ void pause() { pause_v = C::now(); paused = true; } void resume() { if(stopped) throw std::runtime_error("cannot resume a stopped timer"); start_v += C::now() - pause_v; paused = false; stopped = false; } void stop() { end_v = C::now(); stopped = true; } template typename U::rep elapsed() const { /* example: cns::timer t; t.sart(); // do something t.stop(); std::cout << t.elapsed() << std::endl; where UNIT can be: std::chrono::nanoseconds std::chrono::microseconds std::chrono::milliseconds std::chrono::seconds std::chrono::minutes std::chrono::hours */ return (stopped) ? std::chrono::duration_cast(end_v - start_v).count() : (paused) ? std::chrono::duration_cast(pause_v - start_v).count() : std::chrono::duration_cast(C::now() - start_v).count();; } }; } /* namespace cns */ #endif /* TIMER_H */ CppNumericalSolvers-1.0.0/lint.py000077500000000000000000000272011305550306500167770ustar00rootroot00000000000000#!/usr/bin/env python import re import sys import glob import codecs import unicodedata _settings_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh', 'hpp', 'cxx']) _settings_lineWidth = 150 _settings_extraInfo = True _settings_checks = {"studentname": False, "space indentation": True, "line width": True, "space before {": True, "symbol whitespace": True, "trailing whitespace": True, "function definition whitespace": True, "camelCase": True, "endif comment": True, "global namespaces": True, "multiple empty lines": True} regExCache = {} ERROR_COUNTER = 0 class RegExMatcher(object): def __init__(self, matchstring): self.matchstring = matchstring def match(self, regex_expr): if regex_expr not in regExCache: regExCache[regex_expr] = re.compile(regex_expr) self.ans = regExCache[regex_expr].match(self.matchstring) return bool(self.ans) def search(self, regex_expr): if regex_expr not in regExCache: regExCache[regex_expr] = re.compile(regex_expr) self.ans = regExCache[regex_expr].search(self.matchstring) return bool(self.ans) def findall(self, regex_expr): if regex_expr not in regExCache: regExCache[regex_expr] = re.compile(regex_expr) self.ans = regExCache[regex_expr].finditer(self.matchstring) return bool(self.ans) def all(self): return self.ans def span(self): return self.ans.span() def line_width(line): if isinstance(line, unicode): width = 0 for uc in unicodedata.normalize('NFC', line): if unicodedata.east_asian_width(uc) in ('W', 'F'): width += 2 elif not unicodedata.combining(uc): width += 1 return width else: return len(line) def print_error(filename, linenum, category, message, level=0, line="", char=-1): global ERROR_COUNTER ERROR_COUNTER = ERROR_COUNTER + 1 # level 0: warning # level 1: error if level == 0: sys.stderr.write('(line %s): \033[91m[%s]\033[0m %s\n' % (linenum, category, message)) elif level == 1: sys.stderr.write('(line %s): \033[91m[%s]\033[0m %s\n' % (linenum, category, message)) else: sys.stderr.write('(line %s): [%14s] %s\n' % (linenum, category, message)) if _settings_extraInfo: if char != -1: sys.stderr.write('%s %s\n' % (' ' * (10 + 3 + 3 + len(str(linenum))), line)) sys.stderr.write('%s \033[96m%s%s\033[0m\n' % (' ' * (10 + 3 + 3 + len(str(linenum))), '-' * char, '^')) def process_braces(filename, lines, error): ident = [] for line in range(len(lines)): for char in range(len(lines[line])): if lines[line][char] == '{': fullstr = lines[line] startwhites = len(fullstr) - len(fullstr.lstrip()) ident.append((startwhites, line, char + 2)) if lines[line][char] == '}': fullstr = lines[line] startwhites = len(fullstr) - len(fullstr.lstrip()) if len(ident) == 0: msg = 'bracket in line %i at pos %i has no related opening' % (line, char) error(filename, line, 'ident', msg, 1, lines[line], char) break if(ident[len(ident) - 1][1] != line): if(ident[len(ident) - 1][0] != startwhites): msg = 'indentation of bracket in line %i does not match indentation of bracket in line %i' msg = msg % (line, ident[len(ident) - 1][1]) error(filename, line, 'ident', msg) del ident[-1] def process_clean_file_line(filename, file_extension, lines, error, linenum, raw): line = lines[linenum] rline = raw[linenum] # check if line is comment m = RegExMatcher(line) if m.match(r'\s*//'): return if _settings_checks["space before {"]: m = RegExMatcher(line) if m.search(r'\S\{'): if not line[m.span()[1] - 2] == '"': error(filename, linenum, 'braces', 'found no space before {', 1, rline, m.span()[1] - 1) if _settings_checks["symbol whitespace"]: m = RegExMatcher(line) if m.search(r'^([^"]|"[^"]*")*?([,=])\S'): # do not look inside quoted strings # exclude cases == if not line[m.span()[1] - 2:m.span()[1]] == "==": error(filename, linenum, 'whitespace', 'no whitespace after symbol', 1, rline, m.span()[1] - 2) if _settings_checks["function definition whitespace"]: m = RegExMatcher(line) if m.search(r'[^(for)(while)(if)(else)\n&\*/\+\-\|=\,]+\s+\('): # exclude special case with only whitespaces mm = RegExMatcher(line[0:m.span()[1]]) if not mm.search(r'^\s+\('): if not mm.search(r'return\s\(') and not mm.search(r'<\s\('): error(filename, linenum, 'whitespace', 'found whitespace before (', 1, rline, m.span()[1] - 1) if _settings_checks["camelCase"]: m = RegExMatcher(line) if m.search(r'\w+\s*\s[A-Z]+\w*\(.*\{'): error(filename, linenum, 'capitalization', 'found function without camelCase', 1, rline, m.span()[1] - 1) if _settings_checks["global namespaces"]: m = RegExMatcher(line) if m.search(r'using\s+namespace'): error(filename, linenum, 'style', 'do not use global namespaces', 1, rline, m.span()[1] - 1) def process_file_line(filename, file_extension, lines, error, linenum): line = lines[linenum] # check if line is comment m = RegExMatcher(line) if m.match(r'\s*//'): return if _settings_checks["space indentation"]: m = RegExMatcher(line) if m.search('\t'): error(filename, linenum, 'tab', 'use spaces instead of tabs', 1, line, m.span()[1] - 1) if _settings_checks["line width"]: if line_width(line) > _settings_lineWidth: error(filename, linenum, 'line_length', 'lines should be <= %i characters long' % _settings_lineWidth) if _settings_checks["trailing whitespace"]: m = RegExMatcher(line) if m.search(r'\s+$'): error(filename, linenum, 'whitespace', 'found trailing whitespace', 1, line, m.span()[1] - 1) if _settings_checks["endif comment"]: m = RegExMatcher(line) if m.search(r'#endif\s*$'): error(filename, linenum, 'comment', 'found endif without comment', 1, line, m.span()[1] - 1) def process_clean_file_content(filename, file_extension, lines, ignore_lines, error, raw): lines = (['// marker so line numbers and indices both start at 1'] + lines + ['// marker so line numbers end in a known way']) raw = (['// marker so line numbers and indices both start at 1'] + raw + ['// marker so line numbers end in a known way']) for line in range(len(lines)): if line not in ignore_lines: process_clean_file_line(filename, file_extension, lines, error, line, raw) def process_file_content(filename, file_extension, lines, ignore_lines, error): # test if file is ending with empty lines empty_lines = 0 for i in range(len(lines)): if lines[len(lines) - i - 1] == "": empty_lines = empty_lines + 1 else: break if empty_lines == 0: error(filename, len(lines) - empty_lines, 'empty', 'file ends without empty lines') lines = (['// marker so line numbers and indices both start at 1'] + lines + ['// marker so line numbers end in a known way']) for line in range(len(lines)): if line not in ignore_lines: process_file_line(filename, file_extension, lines, error, line) process_braces(filename, lines, error) # http://stackoverflow.com/questions/241327/python-snippet-to-remove-c-and-c-comments def comment_remover(text): def get_newline_chars(str_in): return "" + ("\n" * str_in.count('\n')) def replacer(match): s = match.group(0) if s.startswith('/'): # Matched string is //...EOL or /*...*/ ==> Blot out all non-newline chars return "@#@" + get_newline_chars(s) else: # Matched string is '...' or "..." ==> Keep unchanged return s pattern = re.compile(r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', re.DOTALL | re.MULTILINE) raw = re.sub(pattern, replacer, text) raw = re.sub(r'[ ]*@#@', '', raw) return raw def process_file(filename): error_collection = [] def error_handler(filename, linenum, category, message, level=0, line="", char=-1): error_collection.append((filename, linenum, category, message, level, line, char)) file_extension = filename[filename.rfind('.') + 1:] if file_extension not in _settings_extensions: return raw_source = codecs.open(filename, 'r', 'utf8', 'replace').read() lines = raw_source.split('\n') lf_lines = [] crlf_lines = [] for linenum in range(len(lines) - 1): if lines[linenum].endswith('\r'): lines[linenum] = lines[linenum].rstrip('\r') crlf_lines.append(linenum + 1) else: lf_lines.append(linenum + 1) # check each line if lf_lines and crlf_lines: for linenum in crlf_lines: error_handler(filename, linenum, 'newline', 'use \\n here') # check authorname if _settings_checks["studentname"]: m = RegExMatcher(lines[0]) if not m.match(r'// @student:\s\w+[\s*\w*]*'): msg = 'failed finding name of student in first line. Correct: "// @student: foo"' error_handler(filename, 1, 'content', msg, 1) # check double newline if _settings_checks["multiple empty lines"]: m = RegExMatcher(raw_source) if m.findall(r'(\n[ ]*){3,}'): for mm in m.all(): msg = 'too many empty new lines (count %i)' msg = msg % (-2 + len(raw_source[mm.span()[0]:mm.span()[1]].split('\n'))) error_handler(filename, len(raw_source[:mm.span()[1]].split('\n')) - 1, 'empty', msg, 1) # get lines to ignore ignore_lines = [] pattern = re.compile(r'nolintnextline') m.findall(pattern) for mm in m.all(): ignore_lines.append(len(raw_source[:mm.span()[1]].split('\n')) + 1) pattern = re.compile(r'nolint') m.findall(pattern) for mm in m.all(): ignore_lines.append(len(raw_source[:mm.span()[1]].split('\n'))) process_file_content(filename, file_extension, lines, ignore_lines, error_handler) # clean lines (remove multi line comment) lines = raw_source.split('\n') clines = comment_remover(raw_source).split('\n') process_clean_file_content(filename, file_extension, clines, ignore_lines, error_handler, lines) if len(error_collection): sys.stderr.write('\033[1m\033[93m%10s\033[0m:\n' % (filename)) for e in error_collection: print_error(*e) if __name__ == '__main__': filenames = glob.glob("src/examples/*.cpp") + glob.glob("include/cppoptlib/*/*.h") print("start linting") print(filenames) for filename in filenames: process_file(filename) sys.stderr.write("\n\n") if ERROR_COUNTER == 0: sys.exit(ERROR_COUNTER) else: sys.exit(ERROR_COUNTER) CppNumericalSolvers-1.0.0/matlab/000077500000000000000000000000001305550306500167125ustar00rootroot00000000000000CppNumericalSolvers-1.0.0/matlab/cppoptlib.cxx000066400000000000000000000254361305550306500214440ustar00rootroot00000000000000// disable Testing Macros from G-test #define MATLAB #include #include #include #include "mex.h" #include "../include/cppoptlib/problem.h" #include "../include/cppoptlib/meta.h" #include "../include/cppoptlib/solver/gradientdescentsolver.h" #include "../include/cppoptlib/solver/conjugatedgradientdescentsolver.h" #include "../include/cppoptlib/solver/newtondescentsolver.h" #include "../include/cppoptlib/solver/bfgssolver.h" #include "../include/cppoptlib/solver/lbfgssolver.h" #include "../include/cppoptlib/solver/lbfgsbsolver.h" #include "../include/cppoptlib/solver/cmaessolver.h" #include "../include/cppoptlib/solver/neldermeadsolver.h" using namespace cppoptlib; char *nameObjectiveFunction; char *nameGradientFunction; bool hasGradient = false; char *nameHessianFunction; bool hasHessian = false; size_t in_rows; size_t in_cols; char error_msg[200]; template class MATLABobjective : public BoundedProblem { public: using Superclass = BoundedProblem; using typename Superclass::TVector; using TMatrix = typename Superclass::THessian; T value(const TVector &x) { mxArray * objective_ans, *objective_param[1]; objective_param[0] = mxCreateDoubleMatrix(x.rows(), x.cols(), mxREAL); const T *constVariablePtr = &x(0); memcpy(mxGetPr(objective_param[0]), constVariablePtr, mxGetM(objective_param[0]) * mxGetN(objective_param[0]) * sizeof(*constVariablePtr)); mexCallMATLAB(1, &objective_ans, 1, objective_param, nameObjectiveFunction) ; return mxGetScalar(objective_ans); } void gradient(const TVector &x, TVector &grad) { if (hasGradient) { mxArray * objective_ans, *objective_param[1]; objective_param[0] = mxCreateDoubleMatrix(x.rows(), x.cols(), mxREAL); const double *constVariablePtr = &x(0); memcpy(mxGetPr(objective_param[0]), constVariablePtr, mxGetM(objective_param[0]) * mxGetN(objective_param[0]) * sizeof(*constVariablePtr)); mexCallMATLAB(1, &objective_ans, 1, objective_param, nameGradientFunction) ; size_t r = mxGetM(objective_ans); size_t c = mxGetN(objective_ans); if ((in_rows != r) || (in_cols != c)) { sprintf(error_msg, "Wrong format of gradient! The correct format is %zu x %zu, but %zu x %zu was given", in_rows, in_cols, r, c); mexErrMsgIdAndTxt("MATLAB:cppoptlib", error_msg); } grad = Eigen::Map(mxGetPr(objective_ans), mxGetM(objective_ans) ); } else { this->finiteGradient(x, grad); } } void hessian(const TVector &x, TMatrix & hessian) { if (hasHessian) { mxArray * objective_ans, *objective_param[1]; objective_param[0] = mxCreateDoubleMatrix(x.rows(), x.cols(), mxREAL); const double *constVariablePtr = &x(0); memcpy(mxGetPr(objective_param[0]), constVariablePtr, mxGetM(objective_param[0]) * mxGetN(objective_param[0]) * sizeof(*constVariablePtr)); mexCallMATLAB(1, &objective_ans, 1, objective_param, nameHessianFunction) ; size_t r = mxGetM(objective_ans); size_t c = mxGetN(objective_ans); if ((in_rows != r) || (in_rows != c) || (c != r)) { sprintf(error_msg, "Wrong format of hessian! The correct format is %zu x %zu, but %zu x %zu was given", in_rows, in_rows, r, c); mexErrMsgIdAndTxt("MATLAB:cppoptlib", error_msg); } hessian = Eigen::Map(mxGetPr(objective_ans), mxGetM(objective_ans) , mxGetN(objective_ans)); } else { this->finiteHessian(x, hessian); } } }; void mexFunction(int outLen, mxArray *outArr[], int inLen, const mxArray *inArr[]) { typedef double T; typedef MATLABobjective MATLAB_OBJECTIVE; typedef typename MATLAB_OBJECTIVE::TVector TVector; MATLABobjective f; // check parameters if (inLen < 2) { mexErrMsgIdAndTxt("MATLAB:cppoptlib", "this function need at leat two parameters"); } if (mxGetClassID(inArr[0]) != mxDOUBLE_CLASS) { mexErrMsgIdAndTxt("MATLAB:cppoptlib", "the first arguments needs to be an array of type double"); } // check starting point // ---------------------------------------------------------- in_rows = mxGetM(inArr[0]); in_cols = mxGetN(inArr[0]); if (in_cols > 1 || in_rows == 0) { sprintf(error_msg, "The first argument has to be the inital guess x0 (format: n x 1), but the input format is %zu x %zu", in_rows, in_cols); mexErrMsgIdAndTxt("MATLAB:cppoptlib", error_msg); } auto solution = Eigen::Map(mxGetPr(inArr[0]), mxGetM(inArr[0]) * mxGetN(inArr[0])); auto x = solution.eval(); // check objective function // ---------------------------------------------------------- if (mxGetClassID(inArr[1]) != mxFUNCTION_CLASS) { mexErrMsgIdAndTxt("MATLAB:cppoptlib", "the second arguments has to be the handle of the function (@objective)"); } // get name of objective // ---------------------------------------------------------- mxArray *objective_ans, *objective_param[1]; objective_param[0] = const_cast( inArr[1] ); mexCallMATLAB(1, &objective_ans, 1, objective_param, "char") ; nameObjectiveFunction = mxArrayToString(objective_ans); // parse remaining arguments // ---------------------------------------------------------- enum solver_type {GRADIENTDESCENT, NEWTON, BFGS, LBFGS, LBFGSB, CONJUGATEDGRADIENTDESCENT, CMAES, NELDERMEAD}; solver_type selected_solver = BFGS; if (inLen > 2) { // there are some parameters if ((inLen % 2) != 0) { mexErrMsgIdAndTxt("MATLAB:cppoptlib", "optional arguments have to be passed by 'key','value'."); } for (int arg = 2; arg < inLen; arg += 2) { if (!mxIsChar( inArr[arg])) { mexErrMsgIdAndTxt("MATLAB:cppoptlib", "optional argument keys have to be strings"); } char *key_str = mxArrayToString(inArr[arg]); if (strcmp(key_str, "gradient") == 0) { if (mxGetClassID(inArr[arg + 1]) != mxFUNCTION_CLASS) { mexErrMsgIdAndTxt("MATLAB:cppoptlib", "the argument following 'gradient' has to be a function handle (@gradient)"); } objective_param[0] = const_cast( inArr[arg + 1] ); mexCallMATLAB(1, &objective_ans, 1, objective_param, "char") ; nameGradientFunction = mxArrayToString(objective_ans); hasGradient = true; } if (strcmp(key_str, "hessian") == 0) { if (mxGetClassID(inArr[arg + 1]) != mxFUNCTION_CLASS) { mexErrMsgIdAndTxt("MATLAB:cppoptlib", "the argument following 'hessian' has to be a function handle (@hessian)"); } objective_param[0] = const_cast( inArr[arg + 1] ); mexCallMATLAB(1, &objective_ans, 1, objective_param, "char") ; nameHessianFunction = mxArrayToString(objective_ans); hasHessian = true; } if (strcmp(key_str, "solver") == 0) { if (!mxIsChar( inArr[arg + 1])) { mexErrMsgIdAndTxt("MATLAB:cppoptlib", "solver name has to be a string"); } char *solver_str = mxArrayToString(inArr[arg + 1]); if (strcmp(solver_str, "gradientdescent") == 0) { selected_solver = GRADIENTDESCENT; } else if (strcmp(solver_str, "cg") == 0) { selected_solver = CONJUGATEDGRADIENTDESCENT; } else if (strcmp(solver_str, "bfgs") == 0) { selected_solver = BFGS; } else if (strcmp(solver_str, "l-bfgs") == 0) { selected_solver = LBFGS; } else if (strcmp(solver_str, "l-bfgs-b") == 0) { selected_solver = LBFGSB; } else if (strcmp(solver_str, "newton") == 0) { selected_solver = NEWTON; } else if (strcmp(solver_str, "cmaes") == 0) { selected_solver = CMAES; } else if (strcmp(solver_str, "neldermead") == 0) { selected_solver = NELDERMEAD; } else { sprintf(error_msg, "unknown solver %s", solver_str); mexErrMsgIdAndTxt("MATLAB:cppoptlib", error_msg); } } if (strcmp(key_str, "lb") == 0) { if (mxGetClassID(inArr[arg + 1]) != mxDOUBLE_CLASS) { mexErrMsgIdAndTxt("MATLAB:cppoptlib", "the argument following 'lb' has to be an array of type double"); } size_t lbr = mxGetM(inArr[arg + 1]); size_t lbc = mxGetN(inArr[arg + 1]); if ((in_cols != lbc) || (in_rows != lbr)) { sprintf(error_msg, "expected lowerBound argument format is (format: %zu x 1), but the input format is %zu x %zu", in_rows, lbr, lbc); mexErrMsgIdAndTxt("MATLAB:cppoptlib", error_msg); } auto tmp = Eigen::Map(mxGetPr(inArr[arg + 1]), mxGetM(inArr[arg + 1]) * mxGetN(inArr[arg + 1])); TVector t = tmp; f.setLowerBound(t); } if (strcmp(key_str, "ub") == 0) { if (mxGetClassID(inArr[arg + 1]) != mxDOUBLE_CLASS) { mexErrMsgIdAndTxt("MATLAB:cppoptlib", "the argument following 'lb' has to be an array of type double"); } size_t lbr = mxGetM(inArr[arg + 1]); size_t lbc = mxGetN(inArr[arg + 1]); if ((in_cols != lbc) || (in_rows != lbr)) { sprintf(error_msg, "expected lowerBound argument format is (format: %zu x 1), but the input format is %zu x %zu", in_rows, lbr, lbc); mexErrMsgIdAndTxt("MATLAB:cppoptlib", error_msg); } auto tmp = Eigen::Map(mxGetPr(inArr[arg + 1]), mxGetM(inArr[arg + 1]) * mxGetN(inArr[arg + 1])); TVector t = tmp; f.setUpperBound(t); } } } // solve // ---------------------------------------------------------- switch (selected_solver) { case LBFGS: { LbfgsSolver > solver; solver.minimize(f, x); } break; case LBFGSB: { LbfgsbSolver > solver; solver.minimize(f, x); } break; case BFGS: { BfgsSolver > solver; solver.minimize(f, x); } break; case GRADIENTDESCENT: { GradientDescentSolver > solver; solver.minimize(f, x); } break; case CONJUGATEDGRADIENTDESCENT: { ConjugatedGradientDescentSolver > solver; solver.minimize(f, x); } break; case NEWTON: { NewtonDescentSolver > solver; solver.minimize(f, x); } break; case NELDERMEAD: { NelderMeadSolver > solver; solver.minimize(f, x); } break; case CMAES: { // CMAesSolver > solver; // solver.minimize(f, x); } break; default: mexErrMsgIdAndTxt("MATLAB:cppoptlib:not_implemented", "Your select solver has currently no matlab binding. Oops."); break; } // prepare solution outArr[0] = mxCreateDoubleScalar(f(x)); if (outLen > 1) { outArr[1] = mxCreateDoubleMatrix(x.rows(), x.cols(), mxREAL); double *constVariablePtr = &x(0); memcpy(mxGetPr(outArr[1]), constVariablePtr, mxGetM(outArr[1]) * mxGetN(outArr[1]) * sizeof(*constVariablePtr)); } }CppNumericalSolvers-1.0.0/matlab/example.m000066400000000000000000000044601305550306500205270ustar00rootroot00000000000000clear all clc x0 =[-1 2]'; warning('off','optim:fminunc:SwitchingMethod') fprintf('x0 solver f(x*) x* time \n'); fprintf('--------------------------------------------------------------------\n'); %% -------------------------------------------------------------------------------- fprintf('\n(objective value information only)\n') solver = {'gradientdescent','cg','bfgs','l-bfgs','newton','cmaes','neldermead'}; for s=1:numel(solver) tic [fx,x] = cppoptlib(x0,@rosenbrock,'solver',solver{s}); t = toc; fprintf('(%02.2f,%02.2f) %-15s %03.4f (%06.4f,%06.4f) %f \n', ... x0(1),x0(2), solver{s}, fx,x(1),x(2),t); end tic x = fminsearch(@rosenbrockstruc,x0); t = toc; fprintf('(%02.2f,%02.2f) %-15s %03.4f (%06.4f,%06.4f) %f \n', ... x0(1),x0(2), 'fminsearch', rosenbrock(x),x(1),x(2),t); tic x = fminunc(@rosenbrockstruc,x0); t = toc; fprintf('(%02.2f,%02.2f) %-15s %03.4f (%06.4f,%06.4f) %f \n', ... x0(1),x0(2), 'fminunc', rosenbrock(x),x(1),x(2),t); %% -------------------------------------------------------------------------------- fprintf('\n(with gradient information)\n') solver = {'gradientdescent','cg','bfgs','l-bfgs','l-bfgs-b','newton'}; for s=1:numel(solver) tic [fx,x] = cppoptlib(x0,@rosenbrock,'gradient',@rosenbrock_grad,'solver',solver{s}); t = toc; fprintf('(%02.2f,%02.2f) %-15s %03.4f (%06.4f,%06.4f) %f \n', ... x0(1),x0(2), solver{s}, fx,x(1),x(2),t); end %% -------------------------------------------------------------------------------- fprintf('\n(using finite hessian)\n') solver = {'newton'}; for s=1:numel(solver) tic [fx,x] = cppoptlib(x0,@rosenbrock,'gradient',@rosenbrock_grad,'solver',solver{s}); t = toc; fprintf('(%02.2f,%02.2f) %-15s %03.4f (%06.4f,%06.4f) %f \n', ... x0(1),x0(2), solver{s}, fx,x(1),x(2),t); end %% -------------------------------------------------------------------------------- fprintf('\n(with hessian information)\n') solver = {'newton'}; for s=1:numel(solver) tic [fx,x] = cppoptlib(x0,@rosenbrock,'gradient',@rosenbrock_grad,'hessian',@rosenbrock_hessian,'solver',solver{s}); t = toc; fprintf('(%02.2f,%02.2f) %-15s %03.4f (%06.4f,%06.4f) %f \n', ... x0(1),x0(2), solver{s}, fx,x(1),x(2),t); end warning('on','optim:fminunc:SwitchingMethod') CppNumericalSolvers-1.0.0/matlab/local.m000066400000000000000000000000371305550306500201620ustar00rootroot00000000000000x = fminsearch(@beale,[-1 ,2]);CppNumericalSolvers-1.0.0/matlab/make.m000066400000000000000000000004441305550306500200070ustar00rootroot00000000000000disp('compiling ...'); mex -I./../eigen ... -I./../include ... cppoptlib.cxx CXXFLAGS="\$CXXFLAGS ... -std=c++11" COPTIMFLAGS="\$COPTIMFLAGS ... -O2" ... -output cppoptlib; CppNumericalSolvers-1.0.0/matlab/rosenbrock.m000066400000000000000000000001351305550306500212360ustar00rootroot00000000000000function y = rosenbrock( x ) t1 = (1-x(1)); t2 = (x(2)-x(1)*x(1)); y =t1*t1 + 100*t2*t2; endCppNumericalSolvers-1.0.0/matlab/rosenbrock_grad.m000066400000000000000000000002001305550306500222240ustar00rootroot00000000000000function grad = rosenbrock_grad( x ) grad = [-2*(1-x(1))+200*(x(2)-x(1)*x(1))*(-2*x(1)) ; 200*(x(2)-x(1)*x(1))]; endCppNumericalSolvers-1.0.0/matlab/rosenbrock_hessian.m000066400000000000000000000001571305550306500227540ustar00rootroot00000000000000function hess = rosenbrock_hessian( x ) hess = [ 1200*x(1)*x(1)-400*x(2)+1 , -400*x(1); -400*x(1) , 200]; endCppNumericalSolvers-1.0.0/matlab/rosenbrockstruc.m000066400000000000000000000005011305550306500223140ustar00rootroot00000000000000function [f,g,h] = rosenbrock_struct(x) % Calculate objective f f = 100*(x(2) - x(1)^2)^2 + (1-x(1))^2; if nargout > 1 % gradient required g = [-400*(x(2)-x(1)^2)*x(1)-2*(1-x(1)); 200*(x(2)-x(1)^2)]; end if nargout > 2 % hessian required h = [ 1200*x(1)*x(1)-400*x(2)+1 , -400*x(1); -400*x(1) , 200]; endCppNumericalSolvers-1.0.0/src/000077500000000000000000000000001305550306500162415ustar00rootroot00000000000000CppNumericalSolvers-1.0.0/src/examples/000077500000000000000000000000001305550306500200575ustar00rootroot00000000000000CppNumericalSolvers-1.0.0/src/examples/linearregression.cpp000066400000000000000000000032561305550306500241440ustar00rootroot00000000000000#include #include "../../include/cppoptlib/meta.h" #include "../../include/cppoptlib/problem.h" #include "../../include/cppoptlib/solver/bfgssolver.h" // we define a new problem for optimizing the rosenbrock function // we use a templated-class rather than "auto"-lambda function for a clean architecture template class LinearRegression : public cppoptlib::Problem { public: using typename cppoptlib::Problem::TVector; using MatrixType = Eigen::Matrix; protected: const MatrixType X; const TVector y; const MatrixType XX; public: LinearRegression(const MatrixType &X_, const TVector &y_) : X(X_), y(y_), XX(X_.transpose()*X_) {} T value(const TVector &beta) { return 0.5*(X*beta-y).squaredNorm(); } void gradient(const TVector &beta, TVector &grad) { grad = XX*beta - X.transpose()*y; } }; int main(int argc, char const *argv[]) { typedef LinearRegression TLinearRegression; typedef typename TLinearRegression::TVector TVector; typedef typename TLinearRegression::MatrixType MatrixType; // create true model TVector true_beta = TVector::Random(4); // create data MatrixType X = MatrixType::Random(50, 4); TVector y = X*true_beta; // perform linear regression TLinearRegression f(X, y); TVector beta = TVector::Random(4); std::cout << "start in " << beta.transpose() << std::endl; cppoptlib::BfgsSolver solver; solver.minimize(f, beta); std::cout << "result " << beta.transpose() << std::endl; std::cout << "true model " << true_beta.transpose() << std::endl; return 0; } CppNumericalSolvers-1.0.0/src/examples/logisticregression.cpp000066400000000000000000000035371305550306500245110ustar00rootroot00000000000000#include #include "../../include/cppoptlib/meta.h" #include "../../include/cppoptlib/problem.h" #include "../../include/cppoptlib/solver/bfgssolver.h" // to use this library just use the namespace "cppoptlib" namespace cppoptlib { // we define a new problem for optimizing the rosenbrock function // we use a templated-class rather than "auto"-lambda function for a clean architecture template class LogisticRegression : public Problem { public: using typename Problem::TVector; using MatrixType = Eigen::Matrix; const MatrixType X; const TVector y; const MatrixType XX; LogisticRegression(const MatrixType &X_, const TVector y_) : X(X_), y(y_), XX(X_.transpose()*X_) {} T value(const TVector &beta) { return (1.0/(1.0 + exp(-(X*beta).array())) - y.array()).matrix().squaredNorm(); } void gradient(const TVector &beta, TVector &grad) { const TVector p = 1.0/(1.0 + exp(-(X*beta).array())); grad = X.transpose()*(p-y); } }; } int main(int argc, char const *argv[]) { typedef double T; typedef cppoptlib::LogisticRegression LogReg; typedef typename LogReg::TVector TVector; typedef typename LogReg::MatrixType MatrixType; srand((unsigned int) time(0)); // create true model TVector true_beta = TVector::Random(4); // create data MatrixType X = MatrixType::Random(50, 4); TVector y = 1.0/(1.0 + exp(-(X*true_beta).array())); // perform linear regression LogReg f(X, y); TVector beta = TVector::Random(4); std::cout << "start in " << beta.transpose() << std::endl; cppoptlib::BfgsSolver solver; solver.minimize(f, beta); std::cout << "result " << beta.transpose() << std::endl; std::cout << "true model " << true_beta.transpose() << std::endl; return 0; } CppNumericalSolvers-1.0.0/src/examples/neldermead-customized.cpp000066400000000000000000000114611305550306500250520ustar00rootroot00000000000000#include #include #include #include "../../include/cppoptlib/meta.h" #include "../../include/cppoptlib/problem.h" #include "../../include/cppoptlib/solver/neldermeadsolver.h" static std::ofstream trace_stream; static bool first_iteration = true; // to help generate trace JSON // to use this library just use the namespace "cppoptlib" namespace cppoptlib { // we define a new problem for optimizing the rosenbrock function // we use a templated-class rather than "auto"-lambda function for a clean architecture template class Rosenbrock : public Problem { public: using typename Problem::TVector; using typename Problem::Scalar; using MatrixType = Eigen::Matrix; // this is just the objective (NOT optional) T value(const TVector &x) { const T t1 = (1 - x[0]); const T t2 = (x[1] - x[0] * x[0]); return t1 * t1 + 100 * t2 * t2; } // bool callback(const cppoptlib::Criteria &state, const TVector &x) { // TVector a = x.transpose(); // // Be mindful of calls to value() in the callback if the function is // // computationally intensive.expensive. Consider using detailed_callback(). // std::cout << std::setw(6) << state.iterations << std::setw(12) << a[0] << std::setw(12) << a[1] << std::setw(12) << value(x) << std::endl; // return true; // } bool detailed_callback(const cppoptlib::Criteria &state, SimplexOp op, int index, const MatrixType &x, std::vector f) { TVector xp = x.col(index).transpose(); std::cout << std::setw(6) << state.iterations << std::setw(12) << xp[0] << std::setw(12) << xp[1] << std::setw(12) << f[index] << std::endl; // Write simplex trace TVector x0 = x.col(0).transpose(); TVector x1 = x.col(1).transpose(); TVector x2 = x.col(2).transpose(); trace_stream << (first_iteration ? "" : ",\n") << " {\n" " \"iter\": " << state.iterations << ",\n" " \"op\": \"" << op << "\",\n" " \"index\": " << index << ",\n" " \"x\": [\n" " [" << x0[0] << ", " << x0[1] << "],\n" " [" << x1[0] << ", " << x1[1] << "],\n" " [" << x2[0] << ", " << x2[1] << "]\n" " ],\n" " \"f\": [" << f[0] << ", " << f[1] << ", " << f[2] << "],\n" " \"xDelta\": " << state.xDelta << ",\n" " \"fDelta\": " << state.fDelta << "\n" " }"; first_iteration = false; return true; } }; template class ModifiedNelderMeadSolver: public NelderMeadSolver { public: using Superclass = ISolver; using typename Superclass::Scalar; using typename Superclass::TVector; using MatrixType = Eigen::Matrix; MatrixType makeInitialSimplex(TVector &x) { size_t dim = x.rows(); // create initial simplex MatrixType s = MatrixType::Zero(dim, dim + 1); for (int c = 0; c < int(dim) + 1; ++c) { for (int r = 0; r < int(dim); ++r) { s(r, c) = x(r); if (r == c - 1) { s(r, c) = x(r) + 0.5; } } } NelderMeadSolver::initialSimplexCreated = true; return s; } }; } int main( int argc, char** argv ) { typedef double T; typedef cppoptlib::Rosenbrock Rosenbrock; // Initialize the Rosenbrock-problem Rosenbrock f; // Choose a starting point Rosenbrock::TVector x(2); x << -1, 2; // Choose a solver cppoptlib::ModifiedNelderMeadSolver solver; // Create a Criteria class to set the solver's stop conditions Rosenbrock::TCriteria crit = Rosenbrock::TCriteria::defaults(); crit.iterations = 100; crit.fDelta = 0.0005; solver.setStopCriteria(crit); // Custom method defined in ModifiedNelderMeadSolver above solver.x0 = solver.makeInitialSimplex(x); // Write simplex trace (most of it will be written by the callback) trace_stream.open("simplex-trace.json", std::ofstream::out); trace_stream << "{\n" " \"simplex\": [\n"; // and minimize the function solver.minimize(f, x); // Close trace output trace_stream << "\n" " ],\n" " \"stop\": \"" << solver.stop_condition << "\"\n" "}\n"; trace_stream.close(); // Print results std::cout << std::string(42, '-') << std::endl; std::cout << " stop:" << " " << solver.stop_condition << std::endl; std::cout << " argmin: " << x.transpose() << std::endl; std::cout << " f in argmin: " << f(x) << std::endl; std::cout << " trace written to simplex-trace.json" << std::endl; return 0; } CppNumericalSolvers-1.0.0/src/examples/neldermead.cpp000066400000000000000000000034151305550306500226660ustar00rootroot00000000000000#include #include #include "../../include/cppoptlib/meta.h" #include "../../include/cppoptlib/problem.h" #include "../../include/cppoptlib/solver/neldermeadsolver.h" // to use this library just use the namespace "cppoptlib" namespace cppoptlib { // we define a new problem for optimizing the rosenbrock function // we use a templated-class rather than "auto"-lambda function for a clean architecture template class Rosenbrock : public Problem { public: using typename Problem::TVector; // this is just the objective (NOT optional) T value(const TVector &x) { const T t1 = (1 - x[0]); const T t2 = (x[1] - x[0] * x[0]); return t1 * t1 + 100 * t2 * t2; } bool callback(const cppoptlib::Criteria &state, const TVector &x) { TVector a = x.transpose(); // Be mindful of calls to value() in the callback if the function is // computationally intensive.expensive. Consider using detailed_callback(). std::cout << std::setw(6) << state.iterations << std::setw(12) << a[0] << std::setw(12) << a[1] << std::setw(12) << value(x) << std::endl; return true; } }; } /** * @function main */ int main( int argc, char** argv ) { typedef double T; typedef cppoptlib::Rosenbrock Rosenbrock; // initialize the Rosenbrock-problem Rosenbrock f; // choose a starting point Rosenbrock::TVector x(2); x << -1, 2; // choose a solver cppoptlib::NelderMeadSolver solver; // and minimize the function solver.minimize(f, x); // print argmin std::cout << std::string(42, '-') << std::endl; std::cout << " argmin: " << x.transpose() << std::endl; std::cout << " f in argmin: " << f(x) << std::endl; return 0; } CppNumericalSolvers-1.0.0/src/examples/nonnegls.cpp000066400000000000000000000037111305550306500224100ustar00rootroot00000000000000#include #include "../../include/cppoptlib/meta.h" #include "../../include/cppoptlib/boundedproblem.h" #include "../../include/cppoptlib/solver/lbfgsbsolver.h" // to use CppNumericalSolvers just use the namespace "cppoptlib" namespace cppoptlib { // we will solve ||Xb-y|| s.t. b>=0 template class NonNegativeLeastSquares : public BoundedProblem { public: using Superclass = BoundedProblem; using typename Superclass::TVector; using TMatrix = typename Superclass::THessian; const TMatrix X; const TVector y; public: NonNegativeLeastSquares(const TMatrix &X_, const TVector y_) : Superclass(X_.rows()), X(X_), y(y_) {} T value(const TVector &beta) { return (X*beta-y).dot(X*beta-y); } void gradient(const TVector &beta, TVector &grad) { grad = X.transpose()*2*(X*beta-y); } }; } int main(int argc, char const *argv[]) { const size_t DIM = 4; const size_t NUM = 10; typedef double T; typedef cppoptlib::NonNegativeLeastSquares TNNLS; typedef typename TNNLS::TVector TVector; typedef typename TNNLS::TMatrix TMatrix; // create model X*b for arbitrary b TMatrix X = TMatrix::Random(NUM, DIM); TVector true_beta = TVector::Random(DIM); TMatrix y = X*true_beta; // perform non-negative least squares TNNLS f(X, y); f.setLowerBound(TVector::Zero(DIM)); // create initial guess (make sure it's valid >= 0) TVector beta = TVector::Random(DIM); beta = (beta.array() < 0).select(-beta, beta); std::cout << "true b = " << true_beta.transpose() << "\tloss:" << f(true_beta) << std::endl; std::cout << "start b = " << beta.transpose() << "\tloss:" << f(beta) << std::endl; // init L-BFGS-B for box-constrained solving cppoptlib::LbfgsbSolver solver; solver.minimize(f, beta); std::cout << "final b = " << beta.transpose() << "\tloss:" << f(beta) << std::endl; return 0; } CppNumericalSolvers-1.0.0/src/examples/rosenbrock.cpp000066400000000000000000000041521305550306500227340ustar00rootroot00000000000000#include #include "../../include/cppoptlib/meta.h" #include "../../include/cppoptlib/problem.h" #include "../../include/cppoptlib/solver/bfgssolver.h" // we define a new problem for optimizing the rosenbrock function // we use a templated-class rather than "auto"-lambda function for a clean architecture template class Rosenbrock : public cppoptlib::Problem { public: using typename cppoptlib::Problem::TVector; using typename cppoptlib::Problem::THessian; // this is just the objective (NOT optional) T value(const TVector &x) { const T t1 = (1 - x[0]); const T t2 = (x[1] - x[0] * x[0]); return t1 * t1 + 100 * t2 * t2; } // if you calculated the derivative by hand // you can implement it here (OPTIONAL) // otherwise it will fall back to (bad) numerical finite differences void gradient(const TVector &x, TVector &grad) { grad[0] = -2 * (1 - x[0]) + 200 * (x[1] - x[0] * x[0]) * (-2 * x[0]); grad[1] = 200 * (x[1] - x[0] * x[0]); } // same for hessian (OPTIONAL) // if you want ot use 2nd-order solvers, I encourage you to specify the hessian // finite differences usually (this implementation) behave bad void hessian(const TVector &x, THessian &hessian) { hessian(0, 0) = 1200 * x[0] * x[0] - 400 * x[1] + 1; hessian(0, 1) = -400 * x[0]; hessian(1, 0) = -400 * x[0]; hessian(1, 1) = 200; } }; int main(int argc, char const *argv[]) { // initialize the Rosenbrock-problem typedef Rosenbrock TRosenbrock; TRosenbrock f; // choose a starting point Eigen::VectorXd x(2); x << -1, 2; // first check the given derivative // there is output, if they are NOT similar to finite differences bool probably_correct = f.checkGradient(x); // choose a solver cppoptlib::BfgsSolver solver; // and minimize the function solver.minimize(f, x); // print argmin std::cout << "argmin " << x.transpose() << std::endl; std::cout << "f in argmin " << f(x) << std::endl; return 0; } CppNumericalSolvers-1.0.0/src/examples/rosenbrock_float.cpp000066400000000000000000000053421305550306500241230ustar00rootroot00000000000000#include #include "../../include/cppoptlib/meta.h" #include "../../include/cppoptlib/problem.h" #include "../../include/cppoptlib/solver/bfgssolver.h" #include "../../include/cppoptlib/solver/conjugatedgradientdescentsolver.h" #include "../../include/cppoptlib/solver/newtondescentsolver.h" #include "../../include/cppoptlib/solver/neldermeadsolver.h" #include "../../include/cppoptlib/solver/lbfgssolver.h" #include "../../include/cppoptlib/solver/cmaessolver.h" // to use this library just use the namespace "cppoptlib" namespace cppoptlib { // we define a new problem for optimizing the rosenbrock function // we use a templated-class rather than "auto"-lambda function for a clean architecture template class Rosenbrock : public Problem { public: using typename Problem::TVector; using typename Problem::THessian; // this is just the objective (NOT optional) T value(const TVector &x) { const T t1 = (1 - x[0]); const T t2 = (x[1] - x[0] * x[0]); return t1 * t1 + 100 * t2 * t2; } // if you calculated the derivative by hand // you can implement it here (OPTIONAL) // otherwise it will fall back to (bad) numerical finite differences void gradient(const TVector &x, TVector &grad) { grad[0] = -2 * (1 - x[0]) + 200 * (x[1] - x[0] * x[0]) * (-2 * x[0]); grad[1] = 200 * (x[1] - x[0] * x[0]); } // same for hessian (OPTIONAL) // if you want ot use 2nd-order solvers, I encourage you to specify the hessian // finite differences usually (this implementation) behave bad void hessian(const TVector &x, THessian &hessian) { hessian(0, 0) = 1200 * x[0] * x[0] - 400 * x[1] + 1; hessian(0, 1) = -400 * x[0]; hessian(1, 0) = -400 * x[0]; hessian(1, 1) = 200; } }; } int main(int argc, char const *argv[]) { typedef float T; typedef cppoptlib::Rosenbrock Rosenbrock; // initialize the Rosenbrock-problem Rosenbrock f; // choose a starting point Rosenbrock::TVector x(2); x << -1, 2; // first check the given derivative // there is output, if they are NOT similar to finite differences bool probably_correct = f.checkGradient(x); // choose a solver //cppoptlib::BfgsSolver solver; //cppoptlib::ConjugatedGradientDescentSolver solver; //cppoptlib::NewtonDescentSolver solver; //cppoptlib::NelderMeadSolver solver; cppoptlib::LbfgsSolver solver; //cppoptlib::CMAesSolver solver; // and minimize the function solver.minimize(f, x); // print argmin std::cout << "argmin " << x.transpose() << std::endl; std::cout << "f in argmin " << f(x) << std::endl; return 0; } CppNumericalSolvers-1.0.0/src/examples/simple.cpp000066400000000000000000000024051305550306500220550ustar00rootroot00000000000000#include #include "../../include/cppoptlib/meta.h" #include "../../include/cppoptlib/problem.h" #include "../../include/cppoptlib/solver/bfgssolver.h" // nolintnextline using namespace cppoptlib; // we define a new problem for optimizing the Simple function // we use a templated-class rather than "auto"-lambda function for a clean architecture template class Simple : public Problem { public: using typename Problem::TVector; // this is just the objective (NOT optional) T value(const TVector &x) { return 5*x[0]*x[0] + 100*x[1]*x[1]+5; } // if you calculated the derivative by hand // you can implement it here (OPTIONAL) // otherwise it will fall back to (bad) numerical finite differences void gradient(const TVector &x, TVector &grad) { grad[0] = 2*5*x[0]; grad[1] = 2*100*x[1]; } }; int main(int argc, char const *argv[]) { Simple f; Eigen::VectorXd x(2); x << -1, 2; BfgsSolver> solver; solver.minimize(f, x); std::cout << "f in argmin " << f(x) << std::endl; std::cout << "Solver status: " << solver.status() << std::endl; std::cout << "Final criteria values: " << std::endl << solver.criteria() << std::endl; return 0; } CppNumericalSolvers-1.0.0/src/examples/simple_withoptions.cpp000066400000000000000000000041501305550306500245230ustar00rootroot00000000000000#include #include #include "../../include/cppoptlib/meta.h" #include "../../include/cppoptlib/problem.h" #include "../../include/cppoptlib/solver/gradientdescentsolver.h" // we define a new problem for optimizing the Simple function // we use a templated-class rather than "auto"-lambda function for a clean architecture template class Simple : public cppoptlib::Problem { public: using typename cppoptlib::Problem::TVector; // Inherit the Vector typedef // this is just the objective (NOT optional) T value(const TVector &x) { return 5*x[0]*x[0] + 100*x[1]*x[1]+5; } // if you calculated the derivative by hand // you can implement it here (OPTIONAL) // otherwise it will fall back to (bad) numerical finite differences void gradient(const TVector &x, TVector &grad) { grad[0] = 2*5*x[0]; grad[1] = 2*100*x[1]; } bool callback(const cppoptlib::Criteria &state, const TVector &x) { std::cout << "(" << std::setw(2) << state.iterations << ")" << " ||dx|| = " << std::fixed << std::setw(8) << std::setprecision(4) << state.gradNorm << " ||x|| = " << std::setw(6) << x.norm() << " f(x) = " << std::setw(8) << value(x) << " x = [" << std::setprecision(8) << x.transpose() << "]" << std::endl; return true; } }; int main(int argc, char const *argv[]) { typedef Simple TSimple; TSimple f; typename TSimple::TVector x; x << -10, 2; cppoptlib::Criteria crit = cppoptlib::Criteria::defaults(); // Create a Criteria class to set the solver's stop conditions crit.iterations = 10000; // Increase the number of allowed iterations cppoptlib::GradientDescentSolver solver; solver.setStopCriteria(crit); solver.minimize(f, x); std::cout << "f in argmin " << f(x) << std::endl; std::cout << "Solver status: " << solver.status() << std::endl; std::cout << "Final criteria values: " << std::endl << solver.criteria() << std::endl; return 0; } CppNumericalSolvers-1.0.0/src/test/000077500000000000000000000000001305550306500172205ustar00rootroot00000000000000CppNumericalSolvers-1.0.0/src/test/benchmark.cpp000066400000000000000000000160741305550306500216660ustar00rootroot00000000000000#include #include #include #include "../../gtest/googletest/include/gtest/gtest.h" #include "../../include/cppoptlib/meta.h" #include "../../include/cppoptlib/problem.h" #include "../../include/cppoptlib/solver/gradientdescentsolver.h" #include "../../include/cppoptlib/solver/conjugatedgradientdescentsolver.h" #include "../../include/cppoptlib/solver/newtondescentsolver.h" #include "../../include/cppoptlib/solver/bfgssolver.h" #include "../../include/cppoptlib/solver/lbfgssolver.h" #include "../../include/cppoptlib/solver/lbfgsbsolver.h" #include "../../include/cppoptlib/solver/cmaessolver.h" #include "../../include/cppoptlib/solver/neldermeadsolver.h" #define PRECISION 1e-2 #define PI 3.14159265358979323846 using namespace cppoptlib; // collection of benchmark functions // ---------------------------------------------------------------------------------- template class Rosenbrock : public BoundedProblem { public: using typename Problem::TVector; T value(const TVector &x) { const size_t n = x.rows(); T sum = 0; for (int i = 0; i < n - 1; ++i) { const T t1 = (1 - x[i]); const T t2 = (x[i + 1] - x[i] * x[i]); sum += t1 * t1 + 100 * t2 * t2; } return sum; } }; template class Beale : public BoundedProblem { public: using typename Problem::TVector; T value(const TVector &xx) { const T x = xx[0]; const T y = xx[1]; const T t1 = (1.5 - x + x * y); const T t2 = (2.25 - x + x * y * y); const T t3 = (2.623 - x + x * y * y * y); return t1 * t1 + t2 * t2 + t3 * t3; } void gradient(const TVector &xx, TVector &grad) { const T x = xx[0]; const T y = xx[1]; grad[0] = 2. * (0.15e1 - x + x * y) * (-1. + y) + 2. * (0.225e1 - x + x * y * y) * (y * y - 1.) + 2. * (0.2623e1 - x + x * y*y*y) * (y*y*y - 1.); grad[1] = 2. * (0.15e1 - x + x * y) * x + 0.4e1 * (0.225e1 - x + x * y * y) * x * y + 0.6e1 * (0.2623e1 - x + x * y*y*y) * x * y * y; } }; template class GoldsteinPrice : public BoundedProblem { public: using typename Problem::TVector; T value(const TVector &xx) { const T x = xx[0]; const T y = xx[1]; const T t1 = x+y+1; const T t2 = 19-14*x+3*x*x-14*y+6*x*y+3*y*y; const T t3 = 2*x-3*y; const T t4 = 18-32*x+12*x*x+48*y-36*x*y+27*y*y; return (1+t1*t1*t2)*(30+t3*t3*t4); } }; template class Booth : public BoundedProblem { public: using typename Problem::TVector; T value(const TVector &xx) { const T x = xx[0]; const T y = xx[1]; const T t1 = x+2*y-7; const T t2 = 2*x+y-5;; return t1*t1+t2*t2; } void gradient(const TVector &xx, TVector &grad) { const T x = xx[0]; const T y = xx[1]; grad[0] = 2*(x+2*y-7) + 2*(2*x+y-5)*2; grad[1] = 2*(x+2*y-7)*2 + 2*(2*x+y-5); } }; template class Matyas : public BoundedProblem { public: using typename Problem::TVector; T value(const TVector &xx) { const T x = xx[0]; const T y = xx[1]; return 0.26*(x*x+y*y)-0.48*x*y; } void gradient(const TVector &xx, TVector &grad) { const T x = xx[0]; const T y = xx[1]; grad[0] = 0.26*2*x-0.48*y; grad[1] = 0.26*2*y-0.48*x; } }; template class Levi : public BoundedProblem { public: using typename Problem::TVector; T value(const TVector &xx) { const T x = xx[0]; const T y = xx[1]; return sin(3*PI*x)*sin(3*PI*x)+(x-1)*(x-1)*(1+sin(3*PI*y)*sin(3*PI*y)) +(y-1)*(y-1)*(1+sin(2*PI*y)*sin(2*PI*y)); } void gradient(const TVector &xx, TVector &grad) { const T x = xx[0]; const T y = xx[1]; grad[0] = 0.6e1 * sin(3. * PI * x) * cos(3. * PI * x) * PI + 2. * (x - 1.) * (1. + pow(sin(3. * PI * y), 2.)); grad[1] = 0.6e1 * (double) (int) pow((double) (x - 1), (double) 2) * sin(3. * PI * y) * cos(3. * PI * y) * PI + 2. * (y - 1.) * (1. + pow(sin(2. * PI * y), 2.)) + 0.4e1 * pow(y - 1., 2.) * sin(2. * PI * y) * cos(2. * PI * y) * PI; } }; // define test body // ---------------------------------------------------------------------------------- #define CHECKDIFF(func, a,b) TEST(GradientTest, func) { \ Eigen::Vector2d x; \ x(0) = a; \ x(1) = b; \ func f; \ EXPECT_EQ(true,f.checkGradient(x)); \ } #define BENCH2(sol, func, a,b, fx, y0, y1 ,PREC) TEST(D2Functions##sol, func) { \ Eigen::Vector2d x; \ x(0) = a; \ x(1) = b; \ func f; \ sol> solver; \ solver.minimize(f, x); \ EXPECT_NEAR(fx, f(x), PREC); \ EXPECT_NEAR(y0, x(0), PREC); \ EXPECT_NEAR(y1, x(1), PREC); \ } // optimize and test all different function // ---------------------------------------------------------------------------------- #define BENCHSOVLER(sol) BENCH2(sol, Rosenbrock, -1, 2, 0, 1, 1, PRECISION); \ BENCH2(sol, Beale, -1, 2, 0, 3, 0.5, 0.1); \ BENCH2(sol, GoldsteinPrice, -1, 1.5, 3, 0, -1, PRECISION); \ BENCH2(sol, Booth, -4, 3.7, 0, 1, 3, PRECISION); \ BENCH2(sol, Matyas, -4, 3.7, 0, 0, 0, PRECISION); \ BENCH2(sol, Levi, -4, 3.7, 0, 1, 1, PRECISION); CHECKDIFF(Rosenbrock, -1, 2 ) CHECKDIFF(Beale, -1, 2 ) CHECKDIFF(GoldsteinPrice, -1, 1.5 ) CHECKDIFF(Booth, -4, 3.7 ) CHECKDIFF(Matyas, -4, 3.7 ) CHECKDIFF(Levi, -4, 3.7 ) BENCHSOVLER(GradientDescentSolver) BENCHSOVLER(ConjugatedGradientDescentSolver) BENCHSOVLER(BfgsSolver) BENCHSOVLER(LbfgsSolver) BENCHSOVLER(LbfgsbSolver) BENCHSOVLER(CMAesSolver) BENCHSOVLER(NelderMeadSolver) int main (int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }CppNumericalSolvers-1.0.0/src/test/verify.cpp000066400000000000000000000301401305550306500212260ustar00rootroot00000000000000#undef NDEBUG #include #include #include #include "gtest/gtest.h" #include "include/cppoptlib/meta.h" #include "include/cppoptlib/boundedproblem.h" #include "include/cppoptlib/solver/gradientdescentsolver.h" #include "include/cppoptlib/solver/conjugatedgradientdescentsolver.h" #include "include/cppoptlib/solver/newtondescentsolver.h" #include "include/cppoptlib/solver/bfgssolver.h" #include "include/cppoptlib/solver/lbfgssolver.h" #include "include/cppoptlib/solver/lbfgsbsolver.h" #include "include/cppoptlib/solver/cmaessolver.h" #include "include/cppoptlib/solver/cmaesbsolver.h" #include "include/cppoptlib/solver/neldermeadsolver.h" #define PRECISION 1e-4 using namespace cppoptlib; typedef ::testing::Types MyTypeList; // situation where only have to objective function template class RosenbrockValue : public BoundedProblem { public: using Super = BoundedProblem; using BoundedProblem::BoundedProblem; using typename Super::TVector; Scalar value(const TVector &x) { const Scalar t1 = (1 - x[0]); const Scalar t2 = (x[1] - x[0] * x[0]); return t1 * t1 + 100 * t2 * t2; } }; // now we add the information about the gradient template class RosenbrockGradient : public RosenbrockValue { public: using Super = RosenbrockValue; using RosenbrockValue::RosenbrockValue; using typename Super::TVector; void gradient(const TVector &x, TVector &grad) { grad[0] = -2 * (1 - x[0]) + 200 * (x[1] - x[0] * x[0]) * (-2 * x[0]); grad[1] = 200 * (x[1] - x[0] * x[0]); } }; // now we add the information about the hessian template class RosenbrockFull : public RosenbrockGradient { public: using Super = RosenbrockGradient; using RosenbrockGradient::RosenbrockGradient; using typename Super::TVector; using typename Problem::THessian; void hessian(const TVector &x, typename Super::THessian &hessian) { hessian(0, 0) = 1200 * x[0] * x[0] - 400 * x[1] + 1; hessian(0, 1) = -400 * x[0]; hessian(1, 0) = -400 * x[0]; hessian(1, 1) = 200; } }; template class GradientDescentTest : public testing::Test{}; template class ConjugatedGradientDescentTest : public testing::Test{}; template class NewtonDescentTest : public testing::Test{}; template class BfgsTest : public testing::Test{}; template class LbfgsTest : public testing::Test{}; template class LbfgsbTest : public testing::Test{}; template class CMAesTest : public testing::Test{}; template class CMAesBTest : public testing::Test{}; template class NelderMeadTest : public testing::Test{}; template class CentralDifference : public testing::Test{}; #define SOLVE_PROBLEM( sol, func, a, b, fx ) \ typedef func TProblem;\ TProblem f;\ typename TProblem::TVector x; x << a, b;\ sol solver;\ solver.minimize(f, x);\ EXPECT_NEAR(fx, f(x), PRECISION); #define SOLVE_PROBLEM_F( sol, func, a, b, fx ) \ typedef func TProblem;\ TProblem f;\ typename TProblem::TVector x; x << a, b;\ sol solver;\ solver.minimize(f, x);\ EXPECT_NEAR(fx, f(x), PRECISION); #define SOLVE_PROBLEM_D( sol, func, a, b, fx ) \ typedef func TProblem;\ TProblem f;\ typename TProblem::TVector x; x << a, b;\ sol solver;\ solver.minimize(f, x);\ EXPECT_NEAR(fx, f(x), PRECISION); #define SOLVE_BOUNDED( sol, func, x0, fx, lb, ub ) \ typedef func TProblem;\ TProblem f(lb.cast(), ub.cast());\ sol solver;\ typename TProblem::TVector x = x0.cast();\ solver.minimize(f, x);\ EXPECT_NEAR(fx, f(x), PRECISION); TYPED_TEST_CASE(GradientDescentTest, MyTypeList); TYPED_TEST_CASE(ConjugatedGradientDescentTest, MyTypeList); TYPED_TEST_CASE(NewtonDescentTest, MyTypeList); TYPED_TEST_CASE(BfgsTest, MyTypeList); TYPED_TEST_CASE(LbfgsTest, MyTypeList); TYPED_TEST_CASE(LbfgsbTest, MyTypeList); TYPED_TEST_CASE(CMAesTest, MyTypeList); TYPED_TEST_CASE(CMAesBTest, MyTypeList); TYPED_TEST_CASE(NelderMeadTest, MyTypeList); TYPED_TEST_CASE(CentralDifference, MyTypeList); // only gradient information TEST(GradientDescentTest, RosenbrockFarValue) { SOLVE_PROBLEM_D(cppoptlib::GradientDescentSolver,RosenbrockValue, 15.0, 8.0, 0.0) } TEST(GradientDescentTest, RosenbrockNearValue) { SOLVE_PROBLEM_D(cppoptlib::GradientDescentSolver,RosenbrockValue, -1.0, 2.0, 0.0) } TEST(ConjugatedGradientDescentTest, RosenbrockFarValue) { SOLVE_PROBLEM_D(cppoptlib::ConjugatedGradientDescentSolver,RosenbrockValue, 15.0, 8.0, 0.0) } TEST(ConjugatedGradientDescentTest, RosenbrockNearValue) { SOLVE_PROBLEM_D(cppoptlib::ConjugatedGradientDescentSolver,RosenbrockValue, -1.0, 2.0, 0.0) } TEST(ConjugatedGradientDescentTest, RosenbrockMixValue) { SOLVE_PROBLEM_D(cppoptlib::ConjugatedGradientDescentSolver,RosenbrockValue, -1.2, 100.0, 0.0) } TEST(BfgsTest, RosenbrockFarValue) { SOLVE_PROBLEM_D(cppoptlib::BfgsSolver,RosenbrockValue, 15.0, 8.0, 0.0) } TEST(BfgsTest, RosenbrockNearValue) { SOLVE_PROBLEM_D(cppoptlib::BfgsSolver,RosenbrockValue, -1.0, 2.0, 0.0) } TEST(BfgsTest, RosenbrockMixValue) { SOLVE_PROBLEM_D(cppoptlib::BfgsSolver,RosenbrockValue, -1.2, 100.0, 0.0) } TEST(LbfgsTest, RosenbrockFarValue) { SOLVE_PROBLEM_D(cppoptlib::LbfgsSolver,RosenbrockValue, 15.0, 8.0, 0.0) } TEST(LbfgsTest, RosenbrockNearValue) { SOLVE_PROBLEM_D(cppoptlib::LbfgsSolver,RosenbrockValue, -1.0, 2.0, 0.0) } TEST(LbfgsTest, RosenbrockMixValue) { SOLVE_PROBLEM_D(cppoptlib::LbfgsSolver,RosenbrockValue, -1.2, 100.0, 0.0) } TEST(LbfgsbTest, RosenbrockFarValue) { SOLVE_PROBLEM_D(cppoptlib::LbfgsbSolver,RosenbrockValue, 15.0, 8.0, 0.0) } TEST(LbfgsbTest, RosenbrockNearValue) { SOLVE_PROBLEM_D(cppoptlib::LbfgsbSolver,RosenbrockValue, -1.0, 2.0, 0.0) } TEST(LbfgsbTest, RosenbrockMixValue) { SOLVE_PROBLEM_D(cppoptlib::LbfgsbSolver,RosenbrockValue, -1.2, 100.0, 0.0) } //TEST(CMAesTest, RosenbrockFarValue) { SOLVE_PROBLEM_D(cppoptlib::CMAesSolver,RosenbrockValue, 15.0, 8.0, 0.0) } TEST(CMAesTest, RosenbrockNearValue) { SOLVE_PROBLEM_D(cppoptlib::CMAesSolver,RosenbrockValue, -1.0, 2.0, 0.0) } //TEST(CMAesTest, RosenbrockMixValue) { SOLVE_PROBLEM_D(cppoptlib::CMAesSolver,RosenbrockValue, -1.2, 100.0, 0.0) } TYPED_TEST(NelderMeadTest, RosenbrockFarValue) { SOLVE_PROBLEM(cppoptlib::NelderMeadSolver,RosenbrockValue, 15.0, 8.0, 0.0) } TYPED_TEST(NelderMeadTest, RosenbrockNearValue) { SOLVE_PROBLEM(cppoptlib::NelderMeadSolver,RosenbrockValue, -1.0, 2.0, 0.0) } TYPED_TEST(NelderMeadTest, RosenbrockMixValue) { SOLVE_PROBLEM(cppoptlib::NelderMeadSolver,RosenbrockValue, -1.2, 100.0, 0.0) } // gradient information ( Hessian for newton descent) TYPED_TEST(GradientDescentTest, RosenbrockFarGradient) { SOLVE_PROBLEM(cppoptlib::GradientDescentSolver,RosenbrockGradient, 15.0, 8.0, 0.0) } TYPED_TEST(GradientDescentTest, RosenbrockNearGradient) { SOLVE_PROBLEM(cppoptlib::GradientDescentSolver,RosenbrockGradient, -1.0, 2.0, 0.0) } TYPED_TEST(ConjugatedGradientDescentTest, RosenbrockFarGradient) { SOLVE_PROBLEM(cppoptlib::ConjugatedGradientDescentSolver,RosenbrockGradient, 15.0, 8.0, 0.0) } TYPED_TEST(ConjugatedGradientDescentTest, RosenbrockNearGradient) { SOLVE_PROBLEM(cppoptlib::ConjugatedGradientDescentSolver,RosenbrockGradient, -1.0, 2.0, 0.0) } TYPED_TEST(ConjugatedGradientDescentTest, RosenbrockMixGradient) { SOLVE_PROBLEM(cppoptlib::ConjugatedGradientDescentSolver,RosenbrockGradient, -1.2, 100.0, 0.0) } TYPED_TEST(NewtonDescentTest, RosenbrockFarFull) { SOLVE_PROBLEM(cppoptlib::NewtonDescentSolver,RosenbrockFull, 15.0, 8.0, 0.0) } TYPED_TEST(NewtonDescentTest, RosenbrockNearFull) { SOLVE_PROBLEM(cppoptlib::NewtonDescentSolver,RosenbrockFull, -1.0, 2.0, 0.0) } TYPED_TEST(NewtonDescentTest, RosenbrockMixFull) { SOLVE_PROBLEM(cppoptlib::NewtonDescentSolver,RosenbrockFull, -1.2, 100.0, 0.0) } TYPED_TEST(BfgsTest, RosenbrockFarFull) { SOLVE_PROBLEM(cppoptlib::BfgsSolver,RosenbrockFull, 15.0, 8.0, 0.0) } TYPED_TEST(BfgsTest, RosenbrockNearFull) { SOLVE_PROBLEM(cppoptlib::BfgsSolver,RosenbrockFull, -1.0, 2.0, 0.0) } TYPED_TEST(BfgsTest, RosenbrockMixFull) { SOLVE_PROBLEM(cppoptlib::BfgsSolver,RosenbrockFull, -1.2, 100.0, 0.0) } TYPED_TEST(LbfgsTest, RosenbrockFarFull) { SOLVE_PROBLEM(cppoptlib::LbfgsSolver,RosenbrockFull, 15.0, 8.0, 0.0) } TYPED_TEST(LbfgsTest, RosenbrockNearFull) { SOLVE_PROBLEM(cppoptlib::LbfgsSolver,RosenbrockFull, -1.0, 2.0, 0.0) } TYPED_TEST(LbfgsTest, RosenbrockMixFull) { SOLVE_PROBLEM(cppoptlib::LbfgsSolver,RosenbrockFull, -1.2, 100.0, 0.0) } TYPED_TEST(LbfgsbTest, RosenbrockFarFull) { SOLVE_PROBLEM(cppoptlib::LbfgsbSolver,RosenbrockFull, 15.0, 8.0, 0.0) } TYPED_TEST(LbfgsbTest, RosenbrockNearFull) { SOLVE_PROBLEM(cppoptlib::LbfgsbSolver,RosenbrockFull, -1.0, 2.0, 0.0) } TEST(LbfgsbTest, RosenbrockMixFull) { SOLVE_PROBLEM_D(cppoptlib::LbfgsbSolver,RosenbrockFull, -1.2, 100.0, 0.0) } //TYPED_TEST(CMAesTest, RosenbrockFarFull) { SOLVE_PROBLEM(cppoptlib::CMAesSolver,RosenbrockFull, 15.0, 8.0, 0.0) } TYPED_TEST(CMAesTest, RosenbrockNearFull) { SOLVE_PROBLEM(cppoptlib::CMAesSolver,RosenbrockFull, -1.0, 2.0, 0.0) } //TYPED_TEST(CMAesTest, RosenbrockMixFull) { SOLVE_PROBLEM(cppoptlib::CMAesSolver,RosenbrockFull, -1.2, 100.0, 0.0) } const Eigen::Vector2d NearStart(-1.0, 2.0); const Eigen::Vector2d LowerBound(-3.0, -3.0); const Eigen::Vector2d UpperBound(3.0, 3.0); TYPED_TEST(CMAesBTest, RosenbrockNearFull) { SOLVE_BOUNDED(cppoptlib::CMAesBSolver, RosenbrockFull, NearStart, 0.0, LowerBound, UpperBound) } TYPED_TEST(CentralDifference, Gradient){ // simple function y <- 3*a-b class Func : public Problem { public: using typename Problem::TVector; TypeParam value(const TVector &x) { return 3*x[0]-x[1]; } }; typename Func::TVector x0; x0(0) = 0; x0(1) = 0; Func f; typename Func::TVector grad; // check from fast/bad to slower/better approximation of the gradient for (int accuracy = 0; accuracy < 4; ++accuracy) { f.finiteGradient(x0, grad, accuracy); EXPECT_NEAR(grad(0), 3, PRECISION); EXPECT_NEAR(grad(1), -1, PRECISION); } } TYPED_TEST(CentralDifference, Hessian){ // simple function y <- 3*a^2-a*b class Func : public Problem { public: using typename Problem::TVector; TypeParam value(const TVector &x) { return 3*x[0]*x[0]-x[1]*x[0]; } }; typename Func::TVector x0; x0(0) = 0; x0(1) = 0; Func f; typename Func::THessian hessian; // check using fast version f.finiteHessian(x0, hessian); EXPECT_NEAR(hessian(0,0), 6, PRECISION); EXPECT_NEAR(hessian(1,0), -1, PRECISION); EXPECT_NEAR(hessian(0,1), -1, PRECISION); EXPECT_NEAR(hessian(1,1), 0, PRECISION); // check using slow version f.finiteHessian(x0, hessian,3); EXPECT_NEAR(hessian(0,0), 6, PRECISION); EXPECT_NEAR(hessian(1,0), -1, PRECISION); EXPECT_NEAR(hessian(0,1), -1, PRECISION); EXPECT_NEAR(hessian(1,1), 0, PRECISION); } int main (int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }