I'm interested in having an interface to optimization software from libMesh.
I've attached some code that provides: - A generic optimization solver interface (OptimizationSolver), modeled on NonlinearSolver. - OptimizationSystem, modeled on NonlinearImplicitSystem. - TaoOptimizationSolver, which is a subclass of OptimizationSolver for the specific case of TAO. Note that TAO is included in PETSc now (since version 3.5 I believe). I've implemented a simple test problem in tao_test.C that solves: min 0.5 * U^T A U - U^T. You can run, say, "./example-opt -tao_monitor -tao_view -tao_type nls" and you should get the same solution as if you solved AU = F. If there's broad interest in this, I can clean up the code and turn it into a PR. Some ideas for future additions: - Add support for inequality and equality constraints (inequality constraints are my main motivation for considering optimization problems). I looked into this a bit with TAO and it seems like we have to provide a Jacobian for the constraints, which is a rectangular matrix. - Add interfaces for other optimization suites, e.g. Ipopt or NLOpt David
// The libMesh Finite Element Library. // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // C++ includes // Local Includes #include "optimization_solver.h" #include "tao_optimization_solver.h" namespace libMesh { template <typename T> inline OptimizationSolver<T>::OptimizationSolver (sys_type& s) : ParallelObject(s), objective_object(NULL), gradient_object(NULL), hessian_object(NULL), equality_constraints(NULL), _system(s), _is_initialized (false) { } template <typename T> inline OptimizationSolver<T>::~OptimizationSolver () { this->clear (); } #if defined(LIBMESH_HAVE_PETSC) template <typename T> UniquePtr<OptimizationSolver<T> > OptimizationSolver<T>::build(sys_type& s, const SolverPackage solver_package) { // Build the appropriate solver switch (solver_package) { #ifdef LIBMESH_HAVE_PETSC case PETSC_SOLVERS: return UniquePtr<OptimizationSolver<T> >(new TaoOptimizationSolver<T>(s)); #endif // LIBMESH_HAVE_PETSC default: libmesh_error_msg("ERROR: Unrecognized solver package: " << solver_package); } libmesh_error_msg("We'll never get here!"); return UniquePtr<OptimizationSolver<T> >(); } #else // LIBMESH_HAVE_PETSC template <typename T> UniquePtr<OptimizationSolver<T> > OptimizationSolver<T>::build(sys_type&, const SolverPackage) { libmesh_not_implemented_msg("ERROR: libMesh was compiled without optimization solver support"); } #endif //------------------------------------------------------------------ // Explicit instantiations template class OptimizationSolver<Number>; } // namespace libMesh
// The libMesh Finite Element Library. // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef LIBMESH_OPTIMIZATION_SOLVER_H #define LIBMESH_OPTIMIZATION_SOLVER_H // Local includes #include "libmesh/libmesh_common.h" #include "libmesh/enum_solver_package.h" #include "libmesh/reference_counted_object.h" #include "libmesh/nonlinear_implicit_system.h" #include "libmesh/libmesh.h" #include "libmesh/parallel_object.h" #include "libmesh/auto_ptr.h" #include "optimization_system.h" // C++ includes #include <cstddef> namespace libMesh { // forward declarations template <typename T> class SparseMatrix; template <typename T> class NumericVector; template <typename T> class Preconditioner; /** * This class provides a uniform interface for optimization solvers. This base * class is overloaded to provide nonlinear solvers from different packages * like PETSC. * * @author David Knezevic, 2015 */ template <typename T> class OptimizationSolver : public ReferenceCountedObject<NonlinearSolver<T> >, public ParallelObject { public: /** * The type of system */ typedef OptimizationSystem sys_type; /** * Constructor. Initializes Solver data structures */ explicit OptimizationSolver (sys_type& s); /** * Destructor. */ virtual ~OptimizationSolver (); /** * Builds a \p NonlinearSolver using the nonlinear solver package specified by * \p solver_package */ static UniquePtr<OptimizationSolver<T> > build(sys_type& s, const SolverPackage solver_package = libMesh::default_solver_package()); /** * @returns true if the data structures are * initialized, false otherwise. */ bool initialized () const { return _is_initialized; } /** * Release all memory and clear data structures. */ virtual void clear () {} /** * Initialize data structures if not done so already. */ virtual void init () = 0; /** * Solves the optimization problem. */ virtual void solve () = 0; /** * Prints a useful message about why the latest nonlinear solve * con(di)verged. */ virtual void print_converged_reason() { libmesh_not_implemented(); } /** * Object that computes the objective function f(X) * at the input iterate X. */ OptimizationSystem::ComputeObjective *objective_object; /** * Object that computes the gradient grad_f(X) of the objective function * at the input iterate X. */ OptimizationSystem::ComputeGradient *gradient_object; /** * Object that computes the Hessian H_f(X) of the objective function * at the input iterate X. */ OptimizationSystem::ComputeHessian *hessian_object; /** * Object that computes the equality constraints vector C_eq(X). * This will lead to the constraints C_eq(X) = 0 being imposed. */ OptimizationSystem::ComputeEqualityConstraints *equality_constraints; /** * @returns a constant reference to the system we are using to * define the optimization problem. */ const sys_type & system () const { return _system; } /** * @returns a writeable reference to the system we are using to * define the optimization problem. */ sys_type & system () { return _system; } protected: /** * A reference to the system we are solving. */ sys_type& _system; /** * Flag indicating if the data structures have been initialized. */ bool _is_initialized; }; } // namespace libMesh #endif // LIBMESH_OPTIMIZATION_SOLVER_H
// The libMesh Finite Element Library. // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // C++ includes // Local includes #include "libmesh/equation_systems.h" #include "libmesh/libmesh_logging.h" #include "libmesh/sparse_matrix.h" #include "libmesh/numeric_vector.h" #include "optimization_solver.h" #include "optimization_system.h" namespace libMesh { // ------------------------------------------------------------ // OptimizationSystem implementation OptimizationSystem::OptimizationSystem (EquationSystems& es, const std::string& name_in, const unsigned int number_in) : Parent(es, name_in, number_in), optimization_solver(OptimizationSolver<Number>::build(*this)), C_eq(NumericVector<Number>::build(this->comm())) { } OptimizationSystem::~OptimizationSystem () { // Clear data this->clear(); } void OptimizationSystem::clear () { // clear the optimization solver optimization_solver->clear(); // clear the parent data Parent::clear(); } void OptimizationSystem::reinit () { optimization_solver->clear(); Parent::reinit(); } void OptimizationSystem::initialize_equality_constraints_storage( unsigned int n_eq_constraints) { C_eq->init(n_eq_constraints, false, SERIAL); } void OptimizationSystem::solve () { // Log how long the optimization solve takes. START_LOG("solve()", "OptimizationSystem"); optimization_solver->init(); // Solve the optimization problem. optimization_solver->solve (); // Stop logging the nonlinear solve STOP_LOG("solve()", "OptimizationSystem"); // Update the system after the solve this->update(); } } // namespace libMesh
// The libMesh Finite Element Library. // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef LIBMESH_OPTIMIZATION_SYSTEM_H #define LIBMESH_OPTIMIZATION_SYSTEM_H // Local Includes #include "libmesh/implicit_system.h" // C++ includes namespace libMesh { // Forward declarations template<typename T> class OptimizationSolver; /** * This System subclass enables us to assemble an objective function, * gradient, Hessian and bounds for optimization problems. */ // ------------------------------------------------------------ // OptimizationSystem class definition class OptimizationSystem : public ImplicitSystem { public: /** * Constructor. Optionally initializes required * data structures. */ OptimizationSystem (EquationSystems& es, const std::string& name, const unsigned int number); /** * Destructor. */ virtual ~OptimizationSystem (); /** * The type of system. */ typedef OptimizationSystem sys_type; /** * The type of the parent. */ typedef ImplicitSystem Parent; /** * Abstract base class to be used to calculate the objective * function for optimization. */ class ComputeObjective { public: virtual ~ComputeObjective () {} /** * This function will be called to compute the objective function * to be minimized, and must be implemented by the user in a * derived class. @return the value of the objective function at * the iterate \p X. */ virtual Number objective (const NumericVector<Number>& X, sys_type& S) = 0; }; /** * Abstract base class to be used to calculate the gradient of * an objective function. */ class ComputeGradient { public: virtual ~ComputeGradient () {} /** * This function will be called to compute the gradient of the * objective function, and must be implemented by the user in * a derived class. Set \p grad_f to be the gradient at the * iterate \p X. */ virtual void gradient (const NumericVector<Number>& X, NumericVector<Number>& grad_f, sys_type& S) = 0; }; /** * Abstract base class to be used to calculate the Hessian * of an objective function. */ class ComputeHessian { public: virtual ~ComputeHessian () {} /** * This function will be called to compute the Hessian of * the objective function, and must be implemented by the * user in a derived class. Set \p H_f to be the gradient * at the iterate \p X. */ virtual void hessian (const NumericVector<Number>& X, SparseMatrix<Number>& H_f, sys_type& S) = 0; }; /** * Abstract base class to be used to calculate the equality constraints. */ class ComputeEqualityConstraints { public: virtual ~ComputeEqualityConstraints () {} /** * This function will be called to evaluate the equality constraints * vector C_eq(X). This will impose the constraints C_eq(X) = 0. */ virtual void equality_constraints (const NumericVector<Number>& X, NumericVector<Number>& C_eq, sys_type& S) = 0; }; /** * Initialize the vector and matrix that store the equality constraint * and corresponding Jacobian. */ void initialize_equality_constraint_storage( unsigned int n_eq_constraints, std::vector<unsigned int> n_dofs_per_constraint); /** * @returns a clever pointer to the system. */ sys_type & system () { return *this; } /** * Clear all the data structures associated with * the system. */ virtual void clear (); /** * Reinitializes the member data fields associated with * the system, so that, e.g., \p assemble() may be used. */ virtual void reinit (); /** * Assembles & solves the nonlinear system R(x) = 0. */ virtual void solve (); /** * Initialize storage for the \p n_eq_constraints * equality constraints. */ void initialize_equality_constraints_storage( unsigned int n_eq_constraints); /** * @returns \p "Optimization". Helps in identifying * the system type in an equation system file. */ virtual std::string system_type () const { return "Optimization"; } /** * The \p OptimizationSolver that is used for performing the optimization. */ UniquePtr<OptimizationSolver<Number> > optimization_solver; /** * The vector that stores equality constraints. */ UniquePtr<NumericVector<Number> > C_eq; }; } // namespace libMesh #endif // LIBMESH_OPTIMIZATION_SYSTEM_H
// The libMesh Finite Element Library. // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libmesh/libmesh_common.h" #ifdef LIBMESH_HAVE_PETSC // C++ includes // Local Includes #include "libmesh/petsc_vector.h" #include "libmesh/petsc_matrix.h" #include "libmesh/dof_map.h" #include "tao_optimization_solver.h" namespace libMesh { //-------------------------------------------------------------------- // Functions with C linkage to pass to Tao. Tao will call these // methods as needed. // // Since they must have C linkage they have no knowledge of a namespace. // Give them an obscure name to avoid namespace pollution. extern "C" { //--------------------------------------------------------------- // This function is called by Tao to evaluate objective function at x PetscErrorCode __libmesh_tao_objective (Tao tao, Vec x, PetscReal* objective, void *ctx) { START_LOG("objective()", "TaoOptimizationSolver"); PetscErrorCode ierr = 0; libmesh_assert(x); libmesh_assert(objective); libmesh_assert(ctx); // ctx should be a pointer to the solver (it was passed in as void*) TaoOptimizationSolver<Number>* solver = static_cast<TaoOptimizationSolver<Number>*> (ctx); OptimizationSystem &sys = solver->system(); // We'll use current_local_solution below, so let's ensure that it's consistent // with the vector x that was passed in. PetscVector<Number>& X_sys = *cast_ptr<PetscVector<Number>*>(sys.solution.get()); PetscVector<Number> X(x, sys.comm()); // Perform a swap so that sys.solution points to X X.swap(X_sys); // Impose constraints on X sys.get_dof_map().enforce_constraints_exactly(sys); // Update sys.current_local_solution based on X sys.update(); // Swap back X.swap(X_sys); if (solver->objective_object != NULL) { (*objective) = solver->objective_object->objective( *(sys.current_local_solution), sys); } else { libmesh_error_msg("Objective function not defined in __libmesh_tao_objective"); } STOP_LOG("objective()", "TaoOptimizationSolver"); return ierr; } //--------------------------------------------------------------- // This function is called by Tao to evaluate the gradient at x PetscErrorCode __libmesh_tao_gradient(Tao tao, Vec x, Vec g, void *ctx) { START_LOG("gradient()", "TaoOptimizationSolver"); PetscErrorCode ierr = 0; libmesh_assert(x); libmesh_assert(g); libmesh_assert(ctx); // ctx should be a pointer to the solver (it was passed in as void*) TaoOptimizationSolver<Number>* solver = static_cast<TaoOptimizationSolver<Number>*> (ctx); OptimizationSystem &sys = solver->system(); // We'll use current_local_solution below, so let's ensure that it's consistent // with the vector x that was passed in. PetscVector<Number>& X_sys = *cast_ptr<PetscVector<Number>*>(sys.solution.get()); PetscVector<Number> X(x, sys.comm()); // Perform a swap so that sys.solution points to X X.swap(X_sys); // Impose constraints on X sys.get_dof_map().enforce_constraints_exactly(sys); // Update sys.current_local_solution based on X sys.update(); // Swap back X.swap(X_sys); // We'll also pass the gradient in to the assembly routine // so let's make a PETSc vector for that too. PetscVector<Number> gradient(g, sys.comm()); // Clear the gradient prior to assembly gradient.zero(); if (solver->gradient_object != NULL) { solver->gradient_object->gradient( *(sys.current_local_solution), gradient, sys); } else { libmesh_error_msg("Gradient function not defined in __libmesh_tao_gradient"); } gradient.close(); STOP_LOG("gradient()", "TaoOptimizationSolver"); return ierr; } //--------------------------------------------------------------- // This function is called by Tao to evaluate the Hessian at x PetscErrorCode __libmesh_tao_hessian(Tao tao, Vec x, Mat h, Mat pc, void *ctx) { START_LOG("hessian()", "TaoOptimizationSolver"); PetscErrorCode ierr = 0; libmesh_assert(x); libmesh_assert(hessian); libmesh_assert(pc); libmesh_assert(ctx); // ctx should be a pointer to the solver (it was passed in as void*) TaoOptimizationSolver<Number>* solver = static_cast<TaoOptimizationSolver<Number>*> (ctx); OptimizationSystem &sys = solver->system(); // We'll use current_local_solution below, so let's ensure that it's consistent // with the vector x that was passed in. PetscVector<Number>& X_sys = *cast_ptr<PetscVector<Number>*>(sys.solution.get()); PetscVector<Number> X(x, sys.comm()); // Perform a swap so that sys.solution points to X X.swap(X_sys); // Impose constraints on X sys.get_dof_map().enforce_constraints_exactly(sys); // Update sys.current_local_solution based on X sys.update(); // Swap back X.swap(X_sys); // Let's also set up matrices that will be PetscMatrix<Number> PC(pc, sys.comm()); PetscMatrix<Number> hessian(h, sys.comm()); PC.attach_dof_map(sys.get_dof_map()); hessian.attach_dof_map(sys.get_dof_map()); if (solver->hessian_object != NULL) { // Following PetscNonlinearSolver by passing in PC. It's not clear // why we pass in PC and not hessian though? solver->hessian_object->hessian( *(sys.current_local_solution), PC, sys); } else { libmesh_error_msg("Hessian function not defined in __libmesh_tao_hessian"); } PC.close(); hessian.close(); STOP_LOG("hessian()", "TaoOptimizationSolver"); return ierr; } //--------------------------------------------------------------- // This function is called by Tao to evaluate the equality constraints at x PetscErrorCode __libmesh_tao_equality_constraints(Tao tao, Vec x, Vec ce, void *ctx) { START_LOG("equality_constraints()", "TaoOptimizationSolver"); PetscErrorCode ierr = 0; libmesh_assert(x); libmesh_assert(ce); libmesh_assert(ctx); // ctx should be a pointer to the solver (it was passed in as void*) TaoOptimizationSolver<Number>* solver = static_cast<TaoOptimizationSolver<Number>*> (ctx); OptimizationSystem &sys = solver->system(); // We'll use current_local_solution below, so let's ensure that it's consistent // with the vector x that was passed in. PetscVector<Number>& X_sys = *cast_ptr<PetscVector<Number>*>(sys.solution.get()); PetscVector<Number> X(x, sys.comm()); // Perform a swap so that sys.solution points to X X.swap(X_sys); // Impose constraints on X sys.get_dof_map().enforce_constraints_exactly(sys); // Update sys.current_local_solution based on X sys.update(); // Swap back X.swap(X_sys); // We'll also pass the gradient in to the assembly routine // so let's make a PETSc vector for that too. PetscVector<Number> eq_constraints(ce, sys.comm()); // Clear the gradient prior to assembly eq_constraints.zero(); if (solver->equality_constraints != NULL) { solver->equality_constraints->equality_constraints( *(sys.current_local_solution), eq_constraints, sys); } else { libmesh_error_msg("Constraints function not defined in __libmesh_tao_equality_constraints"); } eq_constraints.close(); STOP_LOG("equality_constraints()", "TaoOptimizationSolver"); return ierr; } } // end extern "C" //--------------------------------------------------------------------- //--------------------------------------------------------------------- // TaoOptimizationSolver<> methods template <typename T> TaoOptimizationSolver<T>::TaoOptimizationSolver (OptimizationSystem& system_in) : OptimizationSolver<T>(system_in), _reason(TAO_CONVERGED_FATOL/*==0*/) // Arbitrary initial value... { } template <typename T> TaoOptimizationSolver<T>::~TaoOptimizationSolver () { this->clear (); } template <typename T> void TaoOptimizationSolver<T>::clear () { if (this->initialized()) { this->_is_initialized = false; PetscErrorCode ierr=0; ierr = TaoDestroy(&_tao); LIBMESH_CHKERRABORT(ierr); } } template <typename T> void TaoOptimizationSolver<T>::init () { // Initialize the data structures if not done so already. if (!this->initialized()) { this->_is_initialized = true; PetscErrorCode ierr=0; ierr = TaoCreate(this->comm().get(),&_tao); LIBMESH_CHKERRABORT(ierr); } } template <typename T> void TaoOptimizationSolver<T>::solve () { START_LOG("solve()", "TaoOptimizationSolver"); this->init (); this->system().solution->zero(); PetscMatrix<T>* hessian = cast_ptr<PetscMatrix<T>*>(this->system().matrix); PetscVector<T>* gradient = cast_ptr<PetscVector<T>*>(this->system().rhs); PetscVector<T>* x = cast_ptr<PetscVector<T>*>(this->system().solution.get()); PetscVector<T>* ce = cast_ptr<PetscVector<T>*>(this->system().C_eq.get()); // Set the starting guess to zero. x->zero(); PetscErrorCode ierr = 0; // Set solution vec and an initial guess ierr = TaoSetInitialVector(_tao, x->vec()); LIBMESH_CHKERRABORT(ierr); // We have to have an objective function libmesh_assert( this->objective_object ); // Set routines for objective, gradient, hessian evaluation ierr = TaoSetObjectiveRoutine(_tao, __libmesh_tao_objective, this); LIBMESH_CHKERRABORT(ierr); if ( this->gradient_object ) { ierr = TaoSetGradientRoutine(_tao, __libmesh_tao_gradient, this); LIBMESH_CHKERRABORT(ierr); } if ( this->hessian_object ) { ierr = TaoSetHessianRoutine(_tao, hessian->mat(), hessian->mat(), __libmesh_tao_hessian, this); LIBMESH_CHKERRABORT(ierr); } // Optionally set equality constraints if ( this->equality_constraints ) { ierr = TaoSetEqualityConstraintsRoutine(_tao, ce->vec(), __libmesh_tao_equality_constraints, this); LIBMESH_CHKERRABORT(ierr); } // Check for Tao command line options ierr = TaoSetFromOptions(_tao); LIBMESH_CHKERRABORT(ierr); // Perform the optimization ierr = TaoSolve(_tao); LIBMESH_CHKERRABORT(ierr); // Store the convergence/divergence reason ierr = TaoGetConvergedReason(_tao, &_reason); LIBMESH_CHKERRABORT(ierr); // Print termination information libMesh::out << "Converged reason: " << _reason << std::endl; if (_reason <= 0) { libMesh::out << "Tao failed to converge." << std::endl; } else { libMesh::out << "Tao converged." << std::endl; } STOP_LOG("solve()", "TaoOptimizationSolver"); } //template <typename T> //void TaoOptimizationSolver<T>::print_converged_reason() //{ // // libMesh::out << "Tao optimization solver convergence/divergence reason: " // << TaoConvergedReasons[this->get_converged_reason()] << std::endl; //} template <typename T> TaoConvergedReason TaoOptimizationSolver<T>::get_converged_reason() { PetscErrorCode ierr=0; if (this->initialized()) { ierr = TaoGetConvergedReason(_tao, &_reason); LIBMESH_CHKERRABORT(ierr); } return _reason; } //------------------------------------------------------------------ // Explicit instantiations template class TaoOptimizationSolver<Number>; } // namespace libMesh #endif // #ifdef LIBMESH_HAVE_PETSC
// The libMesh Finite Element Library. // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef LIBMESH_TAO_OPTIMIZATION_SOLVER_H #define LIBMESH_TAO_OPTIMIZATION_SOLVER_H #include "libmesh/libmesh_config.h" // Petsc include files. #ifdef LIBMESH_HAVE_PETSC // Local includes #include "libmesh/petsc_macro.h" // Include header for the Tao optimization library EXTERN_C_FOR_PETSC_BEGIN # include <petsctao.h> EXTERN_C_FOR_PETSC_END #include "optimization_solver.h" // C++ includes namespace libMesh { // Allow users access to these functions in case they want to reuse them. Note that users shouldn't // need access to these most of the time as they are used internally by this object. extern "C" { PetscErrorCode __libmesh_tao_objective (Tao tao, Vec x, PetscReal* objective, void *ctx); PetscErrorCode __libmesh_tao_gradient(Tao tao, Vec x, Vec g, void *ctx); PetscErrorCode __libmesh_tao_hessian(Tao tao, Vec x, Mat h, Mat pc, void *ctx); PetscErrorCode __libmesh_tao_equality_constraints(Tao tao, Vec x, Vec ce, void *ctx); } /** * This class provides an interface to the Tao optimization solvers. * * @author David Knezevic, 2015 */ template <typename T> class TaoOptimizationSolver : public OptimizationSolver<T> { public: /** * The type of system that we use in conjunction with this solver. */ typedef OptimizationSystem sys_type; /** * Constructor. Initializes Tao data structures. */ explicit TaoOptimizationSolver (sys_type& system); /** * Destructor. */ ~TaoOptimizationSolver (); /** * Release all memory and clear data structures. */ virtual void clear (); /** * Initialize data structures if not done so already. */ virtual void init (); /** * Returns the raw PETSc Tao context pointer. */ Tao tao() { this->init(); return _tao; } /** * Call the Tao solver. */ virtual void solve (); // /** // * Prints a useful message about why the latest nonlinear solve // * con(di)verged. // */ // virtual void print_converged_reason(); /** * Returns the currently-available (or most recently obtained, if the Tao object has * been destroyed) convergence reason. Refer to Tao docs for the meaning of different * TaoConvergedReason. */ TaoConvergedReason get_converged_reason(); protected: /** * Optimization solver context */ Tao _tao; /** * Store the reason for Tao convergence/divergence for use even after _tao * has been cleared. Note that print_converged_reason() will always *try* to * get the current reason with TaoGetConvergedReason(), but if the Tao object * has already been cleared, it will fall back on this stored value. Note that * this value is therefore necessarily *not* cleared by the clear() function. */ TaoConvergedReason _reason; private: friend PetscErrorCode __libmesh_tao_objective (Tao tao, Vec x, PetscReal* objective, void *ctx); friend PetscErrorCode __libmesh_tao_gradient(Tao tao, Vec x, Vec g, void *ctx); friend PetscErrorCode __libmesh_tao_hessian(Tao tao, Vec x, Mat h, Mat pc, void *ctx); friend PetscErrorCode __libmesh_tao_equality_constraints(Tao tao, Vec x, Vec ce, void *ctx); }; } // namespace libMesh #endif // #ifdef LIBMESH_HAVE_PETSC #endif // LIBMESH_TAO_OPTIMIZATION_SOLVER_H
// C++ include files that we need #include <iostream> // libMesh includes #include "libmesh/libmesh.h" #include "libmesh/mesh.h" #include "libmesh/mesh_generation.h" #include "libmesh/exodusII_io.h" #include "libmesh/equation_systems.h" #include "libmesh/fe.h" #include "libmesh/quadrature_gauss.h" #include "libmesh/dof_map.h" #include "libmesh/sparse_matrix.h" #include "libmesh/numeric_vector.h" #include "libmesh/dense_matrix.h" #include "libmesh/dense_vector.h" #include "libmesh/elem.h" #include "libmesh/boundary_info.h" #include "libmesh/string_to_enum.h" #include "libmesh/getpot.h" #include "libmesh/zero_function.h" #include "libmesh/dirichlet_boundaries.h" #include "optimization_system.h" #include "optimization_solver.h" // Bring in everything from the libMesh namespace using namespace libMesh; /** * This class encapsulate all functionality required for assembling * and solving a linear elastic model with contact. */ class AssembleOptimization : public OptimizationSystem::ComputeObjective, public OptimizationSystem::ComputeGradient, public OptimizationSystem::ComputeHessian { private: /** * Keep a reference to the OptimizationSystem. */ OptimizationSystem& _sys; public: /** * Constructor. */ AssembleOptimization( OptimizationSystem &sys_in); /** * The optimization problem we consider here is: * min_U 0.5*U^T A U - U^T F. * In this method, we assemble A and F. */ void assemble_A_and_F(); /** * Evaluate the objective function. */ virtual Number objective ( const NumericVector<Number>& soln, OptimizationSystem& /*sys*/); /** * Evaluate the gradient. */ virtual void gradient ( const NumericVector<Number>& soln, NumericVector<Number>& grad_f, OptimizationSystem& /*sys*/); /** * Evaluate the Hessian. */ virtual void hessian ( const NumericVector<Number>& soln, SparseMatrix<Number>& H_f, OptimizationSystem& /*sys*/); // /** // * Evaluate the vector C_eq(x) that defines equality // * constraints according to C_eq(x) = 0. // */ // virtual void equality_constraints ( // const NumericVector<Number>& soln, // NumericVector<Number>& C_eq, // OptimizationSystem& /*sys*/); /** * Sparse matrix for storing the matrix A. We use * this to facilitate computation of objective, gradient * and hessian. */ SparseMatrix<Number>* A_matrix; /** * Vector for storing F. We use this to facilitate * computation of objective, gradient and hessian. */ NumericVector<Number>* F_vector; }; AssembleOptimization::AssembleOptimization(OptimizationSystem &sys_in) : _sys(sys_in) {} void AssembleOptimization::assemble_A_and_F() { A_matrix->zero(); F_vector->zero(); const MeshBase& mesh = _sys.get_mesh(); const unsigned int dim = mesh.mesh_dimension(); const unsigned int u_var = _sys.variable_number ("u"); const DofMap& dof_map = _sys.get_dof_map(); FEType fe_type = dof_map.variable_type(u_var); UniquePtr<FEBase> fe (FEBase::build(dim, fe_type)); QGauss qrule (dim, fe_type.default_quadrature_order()); fe->attach_quadrature_rule (&qrule); const std::vector<Real>& JxW = fe->get_JxW(); const std::vector<std::vector<Real> >& phi = fe->get_phi(); const std::vector<std::vector<RealGradient> >& dphi = fe->get_dphi(); std::vector<dof_id_type> dof_indices; DenseMatrix<Number> Ke; DenseVector<Number> Fe; MeshBase::const_element_iterator el = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end(); for ( ; el != end_el; ++el) { const Elem* elem = *el; dof_map.dof_indices (elem, dof_indices); const unsigned int n_dofs = dof_indices.size(); fe->reinit (elem); Ke.resize (n_dofs,n_dofs); Fe.resize (n_dofs); for (unsigned int qp=0; qp<qrule.n_points(); qp++) { for (unsigned int dof_i=0; dof_i<n_dofs; dof_i++) { for (unsigned int dof_j=0; dof_j<n_dofs; dof_j++) { Ke(dof_i, dof_j) += JxW[qp] * (dphi[dof_j][qp]* dphi[dof_i][qp]); } Fe(dof_i) += JxW[qp] * phi[dof_i][qp]; } } // This will zero off-diagonal entries of Ke corresponding to // Dirichlet dofs. dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices); // We want the diagonal of constrained dofs to be zero too for(unsigned int local_dof_index=0; local_dof_index<n_dofs; local_dof_index++) { dof_id_type global_dof_index = dof_indices[local_dof_index]; if(dof_map.is_constrained_dof(global_dof_index)) { Ke(local_dof_index,local_dof_index) = 0.; } } A_matrix->add_matrix (Ke, dof_indices); F_vector->add_vector (Fe, dof_indices); } A_matrix->close(); F_vector->close(); } Number AssembleOptimization::objective ( const NumericVector<Number>& soln, OptimizationSystem& /*sys*/) { UniquePtr< NumericVector<Number> > AxU = soln.zero_clone(); A_matrix->vector_mult(*AxU, soln); Real UTxAxU = AxU->dot(soln); Real UTxF = F_vector->dot(soln); return 0.5 * UTxAxU - UTxF; } void AssembleOptimization::gradient ( const NumericVector<Number>& soln, NumericVector<Number>& grad_f, OptimizationSystem& /*sys*/) { grad_f.zero(); // Since we've enforced constaints on soln, A and F, // this automatically sets grad_f to zero for constrained // dofs. A_matrix->vector_mult(grad_f, soln); grad_f.add(-1, *F_vector); } void AssembleOptimization::hessian ( const NumericVector<Number>& soln, SparseMatrix<Number>& H_f, OptimizationSystem& /*sys*/) { H_f.zero(); H_f.add(1., *A_matrix); } //void AssembleOptimization::equality_constraints ( // const NumericVector<Number>& soln, // NumericVector<Number>& C_eq, // OptimizationSystem& /*sys*/) //{ // dof_id_type count = 0; // for(dof_id_type dof_index=0; dof_index<_sys.n_dofs(); dof_index++) // { // if(_sys.get_dof_map().is_constrained_dof(dof_index)) // { // C_eq.set(count, soln(dof_index)); // count++; // } // } //} int main (int argc, char** argv) { LibMeshInit init (argc, argv); GetPot infile("tao_test.in"); const std::string approx_order = infile("approx_order", "FIRST"); const std::string fe_family = infile("fe_family", "LAGRANGE"); Mesh mesh(init.comm()); MeshTools::Generation::build_square (mesh, 10, 10, -1., 1., -1., 1., QUAD9); mesh.print_info(); EquationSystems equation_systems (mesh); OptimizationSystem& system = equation_systems.add_system<OptimizationSystem> ("Optimization"); AssembleOptimization assemble_opt(system); unsigned int u_var = system.add_variable("u", Utility::string_to_enum<Order> (approx_order), Utility::string_to_enum<FEFamily>(fe_family)); system.optimization_solver->objective_object = &assemble_opt; system.optimization_solver->gradient_object = &assemble_opt; system.optimization_solver->hessian_object = &assemble_opt; // Seems like -tao_type ipm is the only solver that can handle // equality constraints. // system.optimization_solver->equality_constraints = &assemble_opt; // system.matrix and system.rhs are used for the gradient and Hessian, // so in this case we add an extra matrix and vector to store A and F. // This makes it easy to write the code for evaluating the objective, // gradient, and hessian. system.add_matrix("A_matrix"); system.add_vector("F_vector"); assemble_opt.A_matrix = &system.get_matrix("A_matrix"); assemble_opt.F_vector = &system.get_vector("F_vector"); // Apply Dirichlet constraints. We can then use DofMap::is_constrained_dof // to figure out which dofs we want to apply equality constraints to. std::set<boundary_id_type> boundary_ids; boundary_ids.insert(0); boundary_ids.insert(1); boundary_ids.insert(2); boundary_ids.insert(3); std::vector<unsigned int> variables; variables.push_back(u_var); ZeroFunction<> zf; DirichletBoundary dirichlet_bc(boundary_ids, variables, &zf); system.get_dof_map().add_dirichlet_boundary(dirichlet_bc); equation_systems.init(); equation_systems.print_info(); assemble_opt.assemble_A_and_F(); // Let's assume that we impose an equality constraint // for every constrained dof in the system system.initialize_equality_constraints_storage(system.n_constrained_dofs()); // We need to close the matrix so that we can use it to store the // Hessian during the solve. system.matrix->close(); system.solve(); std::stringstream filename; ExodusII_IO (mesh).write_equation_systems( "optimization_soln.exo", equation_systems); return 0; }
tao_test.in
Description: Binary data
------------------------------------------------------------------------------ BPM Camp - Free Virtual Workshop May 6th at 10am PDT/1PM EDT Develop your own process in accordance with the BPMN 2 standard Learn Process modeling best practices with Bonita BPM through live exercises http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual- event?utm_ source=Sourceforge_BPM_Camp_5_6_15&utm_medium=email&utm_campaign=VA_SF
_______________________________________________ Libmesh-devel mailing list Libmesh-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/libmesh-devel