diff --git a/dune/solvers/solvers/CMakeLists.txt b/dune/solvers/solvers/CMakeLists.txt index a2e7d1c6b629ea16fb66094512abd83940f8d40f..119244ed6efad8b2bd6d43c9a9d3d937fbf178f9 100644 --- a/dune/solvers/solvers/CMakeLists.txt +++ b/dune/solvers/solvers/CMakeLists.txt @@ -9,4 +9,6 @@ install(FILES solver.hh tcgsolver.cc tcgsolver.hh + trustregionsolver.cc + trustregionsolver.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/solvers/solvers) diff --git a/dune/solvers/solvers/Makefile.am b/dune/solvers/solvers/Makefile.am index fd48820fe5cefad57388951cd1f5e1f2802c88e9..c7d0879fc36b0d41eae4527735482eff033b343a 100644 --- a/dune/solvers/solvers/Makefile.am +++ b/dune/solvers/solvers/Makefile.am @@ -3,7 +3,7 @@ SUBDIRS = solversdir = $(includedir)/dune/solvers/solvers solvers_HEADERS = cgsolver.cc cgsolver.hh iterativesolver.cc iterativesolver.hh \ loopsolver.cc loopsolver.hh quadraticipopt.hh solver.hh \ - tcgsolver.cc tcgsolver.hh + tcgsolver.cc tcgsolver.hh trustregionsolver.cc trustregionsolver.hh EXTRA_DIST = CMakeLists.txt diff --git a/dune/solvers/solvers/trustregionsolver.cc b/dune/solvers/solvers/trustregionsolver.cc new file mode 100644 index 0000000000000000000000000000000000000000..2feb30593812e6a80449af8c02e3d44f9819dae3 --- /dev/null +++ b/dune/solvers/solvers/trustregionsolver.cc @@ -0,0 +1,179 @@ +// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set ts=8 sw=4 et sts=4: +#include <dune/common/bitsetvector.hh> +#include <dune/common/timer.hh> + +#include <dune/istl/io.hh> + +// Using a monotone multigrid as the inner solver +#include <dune/solvers/iterationsteps/mmgstep.hh> +#include <dune/solvers/solvers/loopsolver.hh> + +template <class ProblemType, class VectorType, class MatrixType> +void TrustRegionSolver<ProblemType,VectorType,MatrixType>::solve() +{ + MonotoneMGStep<MatrixType,VectorType>* mgStep = nullptr; + + // if the inner solver is a monotone multigrid set up a max-norm trust-region + if (dynamic_cast<::LoopSolver<VectorType>*>(innerSolver_.get())) { + mgStep = dynamic_cast<MonotoneMGStep<MatrixType,VectorType>*>(dynamic_cast<::LoopSolver<VectorType>*>(innerSolver_.get())->iterationStep_); + + } + + // setup the trust-region in the maximums norm + std::vector<BoxConstraint<field_type,blocksize> > trustRegionObstacles(x_.size()); + for (size_t i=0; i<trustRegionObstacles.size(); i++) + for (int j=0; j<blocksize; j++) { + trustRegionObstacles[i].lower(j) = -initialRadius_; + trustRegionObstacles[i].upper(j) = initialRadius_; + } + + mgStep->obstacles_ = &trustRegionObstacles; + + // ///////////////////////////////////////////////////// + // Trust-Region Solver + // ///////////////////////////////////////////////////// + + double oldEnergy = problem_->energy(x_); + + for (int i=0; i<this->maxIterations_; i++) { + + Dune::Timer totalTimer; + if (this->verbosity_ == Solver::FULL) { + std::cout << "----------------------------------------------------" << std::endl; + std::cout << " Trust-Region Step Number: " << i + << ", radius: " << initialRadius_ + << ", energy: " << oldEnergy << std::endl; + std::cout << "----------------------------------------------------" << std::endl; + } + + // Assemble the quadratic problem + Dune::Timer gradientTimer; + problem_->assembleQP(x_); + + // Compute gradient norm to monitor convergence + VectorType gradient = problem_->f_; + for (size_t j=0; j<gradient.size(); j++) + for (int k=0; k<gradient[j].size(); k++) + if ((*mgStep->ignoreNodes_)[j][k]) + gradient[j][k] = 0; + + if (this->verbosity_ == Solver::FULL) + std::cout << "Gradient norm: " << this->errorNorm_->operator()(gradient) << std::endl; + + if (this->verbosity_ == Solver::FULL) + std::cout << "Assembly took " << gradientTimer.elapsed() << " sec." << std::endl; + + VectorType corr(x_.size()); + corr = 0; + + mgStep->setProblem(problem_->A_, corr, problem_->f_); + + innerSolver_->preprocess(); + + /////////////////////////////// + // Solve ! + /////////////////////////////// + + // Solve the subproblem until we find an acceptable iterate + if (this->verbosity_ == NumProc::FULL) + std::cout << "Solve quadratic problem..." << std::endl; + while(true) { + + Dune::Timer solutionTimer; + innerSolver_->solve(); + if (this->verbosity_ == NumProc::FULL) + std::cout << "Solving the quadratic problem took " << solutionTimer.elapsed() << " seconds." << std::endl; + + corr = mgStep->getSol(); + + if (this->verbosity_ == NumProc::FULL) + std::cout << "Infinity norm of the correction: " << corr.infinity_norm() << std::endl; + + if (corr.infinity_norm() < this->tolerance_) { + if (this->verbosity_ != NumProc::QUIET) + std::cout << i+1 << " trust-region steps were taken." << std::endl; + return; + } + + // //////////////////////////////////////////////////// + // Check whether trust-region step can be accepted + // //////////////////////////////////////////////////// + + VectorType newIterate = x_; + newIterate += corr; + if (this->verbosity_ == NumProc::FULL) + std::cout << "Infinity norm of new iterate : " << newIterate.infinity_norm() << std::endl; + + field_type energy = problem_->energy(newIterate); + + // compute the model decrease + field_type modelDecrease = problem_->modelDecrease(corr); + + field_type relativeModelDecrease = modelDecrease / std::fabs(energy); + + if (this->verbosity_ == NumProc::FULL) { + std::cout << "Absolute model decrease: " << modelDecrease + << ", functional decrease: " << oldEnergy - energy << std::endl; + std::cout << "Relative model decrease: " << relativeModelDecrease + << ", functional decrease: " << (oldEnergy - energy)/energy << std::endl; + } + + // assure that the inner solver works correctly + assert(modelDecrease >= 0); + + if (energy >= oldEnergy) { + if (this->verbosity_ == NumProc::FULL) + printf("No energy descent!\n"); + } + + if (energy >= oldEnergy && + (std::abs((oldEnergy-energy)/energy) < 1e-9 || relativeModelDecrease < 1e-9)) { + if (this->verbosity_ == NumProc::FULL) + std::cout << "Suspecting rounding problems, stopping here!" << std::endl; + + if (this->verbosity_ != NumProc::QUIET) + std::cout << i+1 << " trust-region steps were taken." << std::endl; + + x_ = newIterate; + return; + } + + // ////////////////////////////////////////////// + // Check for acceptance of the step + // ////////////////////////////////////////////// + if ( (oldEnergy-energy) / modelDecrease > 0.9) { + // very successful iteration + + x_ = newIterate; + scaleTrustRegionRadius(trustRegionObstacles,enlargeRadius_); + std::cout << "Very Successful iteration, enlarging radius! " <<initialRadius_<<std::endl; + + // current energy becomes 'oldEnergy' for the next iteration + oldEnergy = energy; + break; + + } else if ( (oldEnergy-energy) / modelDecrease > 0.01 + || std::abs(oldEnergy-energy) < 1e-12) { + // successful iteration + x_ = newIterate; + + // current energy becomes 'oldEnergy' for the next iteration + oldEnergy = energy; + break; + + } else { + + // unsuccessful iteration + + // Decrease the trust-region radius + scaleTrustRegionRadius(trustRegionObstacles,shrinkRadius_); + + if (this->verbosity_ == NumProc::FULL) + std::cout << "Unsuccessful iteration, shrinking radius! " <<initialRadius_<<std::endl; + } + } + std::cout << "iteration took " << totalTimer.elapsed() << " sec." << std::endl; + } + +} diff --git a/dune/solvers/solvers/trustregionsolver.hh b/dune/solvers/solvers/trustregionsolver.hh new file mode 100644 index 0000000000000000000000000000000000000000..9438b23ea2ad07aae79bfbdbbece0cd393817345 --- /dev/null +++ b/dune/solvers/solvers/trustregionsolver.hh @@ -0,0 +1,147 @@ +// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set ts=8 sw=4 et sts=4: +#ifndef DUNE_SOLVERS_SOLVERS_TRUST_REGION_SOLVER_HH +#define DUNE_SOLVERS_SOLVERS_TRUST_REGION_SOLVER_HH + +#include <vector> + +#include <dune/common/bitsetvector.hh> + +#include <dune/solvers/common/boxconstraint.hh> +#include <dune/solvers/solvers/iterativesolver.hh> +#include <dune/solvers/solvers/loopsolver.hh> + +/** \brief A standard Trust-region solver with infinity-norm for the trust-region constraints, to solve an unconstrained nonlinear (possibly nonconvex) minimization problem. + * + * The energy functional is provided by the ProblemType class. It has to provide the following methods: + * + * void assembleQP(const VectorType& iterate) + * field_type energy(const VectorType& iterate) + * field_type modelDecrease(const VectorType& iterate) + * + * and member: + * MatrixType A_ + * VectorType f_ + * + * The method 'assembleQP' should assemble the local quadratic approximation at 'iterate' + * The local model is to be given by the quadratic part A_ and the linear part f_ + * + * The method 'energy' should evaluate the nonlinear energy at 'iterate'. + * + * The method 'modelDecrease' should compute the decrease in the model-energy. + * + * The inner solver that has to be provided should be able to solve non-convex box-constrained quadratic problems, + * if the nonlinear functional is non-convex itself. + * + * \tparam ProblemType A class representing the energy functional + * + * */ +template <class ProblemType, class VectorType, class MatrixType> +class TrustRegionSolver + : public IterativeSolver<VectorType> +{ + const static int blocksize = VectorType::value_type::dimension; + typedef IterativeSolver<VectorType> Base; + typedef typename VectorType::field_type field_type; + +public: + + TrustRegionSolver() + : Base(0,100,NumProc::FULL), enlargeRadius_(2), shrinkRadius_(0.5), + goodIteration_(0.9), badIteration_(0.01) + {} + + TrustRegionSolver(field_type tolerance, int maxIterations, NumProc::VerbosityMode verbosity=NumProc::FULL) + : Base(tolerance, maxIterations, verbosity), enlargeRadius_(2), shrinkRadius_(0.5), + goodIteration_(0.9), badIteration_(0.01) + {} + + TrustRegionSolver(const Dune::ParameterTree& trConfig) + : Base(0,100,NumProc::FULL) + { + setupTrParameter(trConfig); + } + + //! Setup the standard trust-region parameter + void setupTrParameter(const Dune::ParameterTree& trConfig) { + + this->tolerance_ = trConfig.get<field_type>("tolerance"); + this->maxIterations_ = trConfig.get<int>("maxIterations"); + this->verbosity_ = trConfig.get("verbosity",NumProc::FULL); + initialRadius_ = trConfig.get<field_type>("initialRadius"); + maxRadius_ = trConfig.get<field_type>("maxRadius"); + enlargeRadius_ = trConfig.get("enlargeRadius",2.0); + shrinkRadius_ = trConfig.get("shrinkRadius",0.5); + goodIteration_ = trConfig.get("goodIteration",0.9); + badIteration_ = trConfig.get("badIteration",0.01); + + } + + + /** \brief Set up the solver. */ + void setup(const Dune::ParameterTree& trConfig, + Norm<VectorType>& errorNorm, + ProblemType& problem, Solver& innerSolver) { + + setupTrParameter(trConfig); + this->errorNorm_ = &errorNorm; + problem_ = Dune::stackobject_to_shared_ptr(problem); + innerSolver_ = Dune::stackobject_to_shared_ptr(innerSolver); + } + + //! Solve the problem. + void solve(); + + //! Set the initial iterate + void setInitialIterate(const VectorType& x) {x_ = x;} + + VectorType getSol() const {return x_;} + +protected: + + //! Adjust the trust-region obstacles + void scaleTrustRegionRadius(std::vector<BoxConstraint<field_type,blocksize> >& obs, const field_type& scaling) + { + if (scaling*initialRadius_>maxRadius_) + initialRadius_ = maxRadius_; + else + initialRadius_ *= scaling; + + for (size_t i=0; i<obs.size(); i++) + for (int j=0; j<blocksize; j++) { + obs[i].lower(j) = -initialRadius_; + obs[i].upper(j) = initialRadius_; + } + } + + /** \brief The problem type. */ + std::shared_ptr<ProblemType> problem_; + + /** \brief The solution vector */ + VectorType x_; + + /** \brief The solver for the quadratic inner problems */ + std::shared_ptr<Solver> innerSolver_; + + /** \brief The initial trust-region radius in the maximum-norm */ + field_type initialRadius_; + + /** \brief The maximal trust-region radius in the maximum-norm */ + field_type maxRadius_; + + /** \brief If the iteration is very successfull we enlarge the radius by this factor.*/ + field_type enlargeRadius_; + + /** \brief If the iteration is unsuccessfull we shrink the radius by this factor.*/ + field_type shrinkRadius_; + + /** \brief If the ratio between predicted and achieved decrease is above this number, the iteration is very successful.*/ + field_type goodIteration_; + + /** \brief If the ratio between predicted and achieved decrease is lower than this number, the iteration is unsuccessful.*/ + field_type badIteration_; +}; + +#include "trustregionsolver.cc" + +#endif