Commit 2c20415f authored by Patric Schmitz's avatar Patric Schmitz

IPOPTSolverLean becomes IPOPTSolver

removing redundant IPOPTSolver implementation
parent 45f2b252
Pipeline #12797 passed with stages
in 7 minutes and 15 seconds
......@@ -114,14 +114,13 @@ int main(void)
std::cout << "---------- 5) Solve with IPOPT solver... " << std::endl;
COMISO::IPOPTSolver ipopt;
ipopt.app().Options()->SetStringValue("derivative_test", "second-order");
ipopt.set_ipopt_option("derivative_test", "second-order");
ipopt.solve(&lsqp, constraints);
#endif
std::cout << "---------- 6) Print solution..." << std::endl;
for( int i=0; i<n; ++i)
std::cerr << "x_" << i << " = " << lsqp.x()[i] << std::endl;
return 0;
}
This diff is collapsed.
......@@ -8,134 +8,144 @@
#ifndef COMISO_IPOPTSOLVER_HH
#define COMISO_IPOPTSOLVER_HH
//== COMPILE-TIME PACKAGE REQUIREMENTS ========================================
#include <CoMISo/Config/config.hh>
#if COMISO_IPOPT_AVAILABLE
//== INCLUDES =================================================================
#include <CoMISo/Config/CoMISoDefines.hh>
#include <CoMISo/Utils/StopWatch.hh>
#include <CoMISo/Utils/gmm.hh>
#include <vector>
#include <cstddef>
#include "NProblemGmmInterface.hh"
#include "NProblemInterface.hh"
#include "NConstraintInterface.hh"
#include "BoundConstraint.hh"
#include "IPOPTProblemInstance.hh"
#include <IpTNLP.hpp>
#include <IpIpoptApplication.hpp>
#include <IpSolveStatistics.hpp>
#include <functional>
#include <vector>
#include <string>
//== FORWARDDECLARATIONS ======================================================
#include <CoMISo/Config/CoMISoDefines.hh>
//== NAMESPACES ===============================================================
namespace COMISO {
//== FORWARDDECLARATIONS ======================================================
class NProblemInterface;
class NConstraintInterface;
struct IPOPTCallbackParameters;
//== CLASS DEFINITION =========================================================
/** \class IPOPTSolver
Solver for Interior Point optimization problems.
/** \class NewtonSolver NewtonSolver.hh <ACG/.../NewtonSolver.hh>
Solves an interior point problem, given an NProblemInterface
instance and optionally a set of constraints as well as "lazy
constraints" via NConstraintInterface.
Brief Description.
A more elaborate description follows.
Lazy constraints are not active while the initial solution to the
problem is computed. After the first solution is found, the lazy
constraints are checked and added to the set of active constraints
if they are violated. This process is then repeated until all
constraints are satisfied OR a maximum number of solution attempts
has been reached. In that case the optimization is started once
more, with all lazy constraints active.
*/
class COMISODLLEXPORT IPOPTSolver
{
public:
/// Default constructor -> set up IpOptApplication
IPOPTSolver();
// ********** SOLVE **************** //
// solve -> returns ipopt status code
//------------------------------------------------------
// enum ApplicationReturnStatus
// {
// Solve_Succeeded=0,
// Solved_To_Acceptable_Level=1,
// Infeasible_Problem_Detected=2,
// Search_Direction_Becomes_Too_Small=3,
// Diverging_Iterates=4,
// User_Requested_Stop=5,
// Feasible_Point_Found=6,
//
// Maximum_Iterations_Exceeded=-1,
// Restoration_Failed=-2,
// Error_In_Step_Computation=-3,
// Maximum_CpuTime_Exceeded=-4,
// Not_Enough_Degrees_Of_Freedom=-10,
// Invalid_Problem_Definition=-11,
// Invalid_Option=-12,
// Invalid_Number_Detected=-13,
//
// Unrecoverable_Exception=-100,
// NonIpopt_Exception_Thrown=-101,
// Insufficient_Memory=-102,
// Internal_Error=-199
// };
//------------------------------------------------------
int solve(NProblemInterface* _problem, const std::vector<NConstraintInterface*>& _constraints);
// same as above with additional lazy constraints that are only added iteratively to the problem if not satisfied
int solve(NProblemInterface* _problem,
const std::vector<NConstraintInterface*>& _constraints,
const std::vector<NConstraintInterface*>& _lazy_constraints,
const double _almost_infeasible = 0.5,
const int _max_passes = 5 );
// for convenience, if no constraints are given
int solve(NProblemInterface* _problem);
// deprecated interface for backwards compatibility
int solve(NProblemGmmInterface* _problem, std::vector<NConstraintInterface*>& _constraints);
// ********* CONFIGURATION ********************* //
// access the ipopt-application (for setting parameters etc.)
// examples: app().Options()->SetIntegerValue("max_iter", 100);
// app().Options()->SetStringValue("derivative_test", "second-order");
// app().Options()->SetStringValue("hessian_approximation", "limited-memory");
IPOPTSolver();
~IPOPTSolver();
// *********** OPTIONS **************//
/*!
Set options of the underlying ipopt solver.
For a thorough list and documentation of available options, refer
to: https://www.coin-or.org/Ipopt/documentation/node40.html
*/
void set_ipopt_option(std::string, const int&);
void set_ipopt_option(std::string, const double&);
void set_ipopt_option(std::string, const std::string&);
/*!
Get options of the underlying ipopt solver.
The type of option {int, double, std::string} needs to be passed as
template argument.
*/
template<typename T>
T get_ipopt_option(std::string option);
/*!
Set the maximum number of iterations
*/
void set_max_iterations(const int _max_iterations);
int get_max_iterations() const;
/*! Set the threshold on the lazy inequality constraint to decide
if we are near the constraint boundary.
*/
void set_almost_infeasible_threshold(const double _alm_infsb_thrsh);
double get_almost_infeasible_threshold() const;
/*!
Set the max number of incremental lazy constraint iterations before switching
to the fully constrained problem.
\note The default value is 5.
*/
void set_incremental_lazy_constraint_max_iteration_number
(const int _incr_lazy_cnstr_max_iter_nmbr);
int get_incremental_lazy_constraint_max_iteration_number() const;
/*
Turn on/off solving the fully constraint problem after exhausting the
incremental lazy constraint iterations.
\note The default value of this is true.
*/
void set_enable_all_lazy_contraints(const bool _enbl_all_lzy_cnstr);
bool get_enable_all_lazy_contraints() const;
/*
Set intermediate callback function object. For the definition of
IPOPTCallbackParameters include the IPOPTCallbackParameters.hh
header.
If the callback function returns false, IPOPT will terminate
prematurely with the User_Requested_Stop status.
*/
void set_callback_function
(std::function<bool(const IPOPTCallbackParameters &)>);
Ipopt::IpoptApplication& app() {return (*app_); }
// ********** SOLVE **************** //
//! Solve a problem instance with an optional set of constraints.
//! \throws Outcome
void solve
(NProblemInterface* _problem,
const std::vector<NConstraintInterface*>& _constraints = {});
void set_print_level(const int _level)
{
app().Options()->SetIntegerValue("print_level", _level);
print_level_ = _level;
}
//! Same as above with additional lazy constraints that are only
//! added iteratively to the problem if not satisfied.
//! \throws Outcome
void solve
(NProblemInterface* _problem,
const std::vector<NConstraintInterface*>& _constraints,
const std::vector<NConstraintInterface*>& _lazy_constraints);
protected:
double* P(std::vector<double>& _v)
{
if( !_v.empty())
return ((double*)&_v[0]);
else
return 0;
}
//! Get the computed solution energy
double energy();
private:
class Impl;
Impl* impl_;
// smart pointer to IpoptApplication to set options etc.
Ipopt::SmartPtr<Ipopt::IpoptApplication> app_;
int print_level_;
// inhibit copy
IPOPTSolver(const IPOPTSolver&);
IPOPTSolver& operator=(const IPOPTSolver&);
};
//=============================================================================
} // namespace COMISO
//=============================================================================
#endif // COMISO_IPOPT_AVAILABLE
//=============================================================================
#endif // ACG_IPOPTSOLVER_HH defined
#endif // COMISO_IPOPTSOLVER_HH defined
//=============================================================================
//=============================================================================
//
// CLASS IPOPTSolverLean - IMPLEMENTATION
//
//=============================================================================
//== INCLUDES =================================================================
//== COMPILE-TIME PACKAGE REQUIREMENTS ========================================
#include <CoMISo/Config/config.hh>
#if COMISO_IPOPT_AVAILABLE
//=============================================================================
#include "NProblemGmmInterface.hh"
#include "NProblemInterface.hh"
#include "NConstraintInterface.hh"
#include "BoundConstraint.hh"
#include "CoMISo/Utils/CoMISoError.hh"
#include <Base/Debug/DebConfig.hh>
#include <Base/Debug/DebTime.hh>
#include <CoMISo/Utils/gmm.hh>
#include <IpTNLP.hpp>
#include <IpIpoptApplication.hpp>
#include <IpSolveStatistics.hpp>
#include "IPOPTProblemInstance.hh"
#include "IPOPTSolverLean.hh"
//== NAMESPACES ===============================================================
namespace COMISO {
//== IMPLEMENTATION ===========================================================
class IPOPTSolverLean::Impl
{
public:
Impl()
: app_(IpoptApplicationFactory()), // Create an instance of IpoptApplication
alm_infsb_thrsh_(0.5),
incr_lazy_cnstr_max_iter_nmbr_(5),
enbl_all_lzy_cnstr_(true)
{
setup_ipopt_defaults();
}
void set_ipopt_option(std::string, const int&);
void set_ipopt_option(std::string, const double&);
void set_ipopt_option(std::string, const std::string&);
template <typename T>
T get_ipopt_option(std::string);
private:
void setup_ipopt_defaults();
public:
Ipopt::SmartPtr<Ipopt::IpoptApplication> app_;
std::function<bool(const IPOPTCallbackParameters &)> intermediate_callback_;
double alm_infsb_thrsh_;
int incr_lazy_cnstr_max_iter_nmbr_;
bool enbl_all_lzy_cnstr_;
private:
// default ipopt options
static const std::string ipopt_default_hsl_solver;
static const int ipopt_default_max_iter;
static const int ipopt_default_mumps_mem_percent;
};
const std::string IPOPTSolverLean::Impl::ipopt_default_hsl_solver = "ma57";
const int IPOPTSolverLean::Impl::ipopt_default_max_iter = 200;
const int IPOPTSolverLean::Impl::ipopt_default_mumps_mem_percent = 5;
void
IPOPTSolverLean::Impl::
set_ipopt_option
(std::string option, const int& value)
{
app_->Options()->SetIntegerValue(option, value);
}
void
IPOPTSolverLean::Impl::
set_ipopt_option
(std::string option, const double& value)
{
app_->Options()->SetNumericValue(option, value);
}
void
IPOPTSolverLean::Impl::
set_ipopt_option
(std::string option, const std::string& value)
{
app_->Options()->SetStringValue(option, value);
}
template <typename T> T
IPOPTSolverLean::Impl::
get_ipopt_option(std::string)
{
// @TODO print warning about unsupported option type!
}
template <> int
IPOPTSolverLean::Impl::
get_ipopt_option<int>(std::string option)
{
int value;
app_->Options()->GetIntegerValue(option, value, "");
return value;
}
template <> double
IPOPTSolverLean::Impl::
get_ipopt_option<double>(std::string option)
{
double value;
app_->Options()->GetNumericValue(option, value, "");
return value;
}
template <> std::string
IPOPTSolverLean::Impl::
get_ipopt_option<std::string>(std::string option)
{
std::string value;
app_->Options()->GetStringValue(option, value, "");
return value;
}
void IPOPTSolverLean::Impl::setup_ipopt_defaults()
{
// Switch to HSL if available
#if COMISO_HSL_AVAILABLE
set_ipopt_option("linear_solver", ipopt_default_hsl_solver);
#else
set_ipopt_option("linear_solver", "mumps");
#endif
#ifdef DEB_ON
if (!Debug::Config::query().console())
#endif
{// Block any output on cout and cerr from Ipopt.
set_ipopt_option("suppress_all_output", "yes");
}
#ifdef WIN32
// Restrict memory to be able to run larger problems on windows
// with the default mumps solver
// TODO: find out what this does and whether it makes sense to do it
set_ipopt_option("mumps_mem_percent",
ipopt_default_mumps_mem_percent);
#endif
// set maximum solver iterations
set_ipopt_option("max_iter", ipopt_default_max_iter);
}
//-----------------------------------------------------------------------------
IPOPTSolverLean::IPOPTSolverLean()
: impl_(new Impl)
{
}
IPOPTSolverLean::
~IPOPTSolverLean()
{
delete impl_;
}
void
IPOPTSolverLean::
set_ipopt_option
(std::string option, const int& value)
{
impl_->set_ipopt_option(option, value);
}
void
IPOPTSolverLean::
set_ipopt_option
(std::string option, const double& value)
{
impl_->set_ipopt_option(option, value);
}
void
IPOPTSolverLean::
set_ipopt_option
(std::string option, const std::string& value)
{
impl_->set_ipopt_option(option, value);
}
template <typename T> T
IPOPTSolverLean::
get_ipopt_option(std::string option)
{
return impl_->get_ipopt_option<T>(option);
}
template int IPOPTSolverLean::get_ipopt_option<int>(std::string);
template double IPOPTSolverLean::get_ipopt_option<double>(std::string);
template std::string IPOPTSolverLean::get_ipopt_option<std::string>(std::string);
void
IPOPTSolverLean::
set_max_iterations
(const int _max_iterations)
{
impl_->set_ipopt_option("max_iter", _max_iterations);
}
int
IPOPTSolverLean::
get_max_iterations() const
{
return impl_->get_ipopt_option<int>("max_iter");
}
void
IPOPTSolverLean::
set_almost_infeasible_threshold
(const double _alm_infsb_thrsh)
{
impl_->alm_infsb_thrsh_ = _alm_infsb_thrsh;
}
double
IPOPTSolverLean::
get_almost_infeasible_threshold() const
{
return impl_->alm_infsb_thrsh_;
}
void
IPOPTSolverLean::
set_incremental_lazy_constraint_max_iteration_number
(const int _incr_lazy_cnstr_max_iter_nmbr)
{
impl_->incr_lazy_cnstr_max_iter_nmbr_ = _incr_lazy_cnstr_max_iter_nmbr;
}
int
IPOPTSolverLean::
get_incremental_lazy_constraint_max_iteration_number() const
{
return impl_->incr_lazy_cnstr_max_iter_nmbr_;
}
void
IPOPTSolverLean::
set_enable_all_lazy_contraints
(const bool _enbl_all_lzy_cnstr)
{
impl_->enbl_all_lzy_cnstr_ = _enbl_all_lzy_cnstr;
}
bool
IPOPTSolverLean::
get_enable_all_lazy_contraints() const
{
return impl_->enbl_all_lzy_cnstr_;
}
void
IPOPTSolverLean::
set_callback_function
(std::function<bool(const IPOPTCallbackParameters &)> func)
{
impl_->intermediate_callback_ = func;
}
static void
throw_ipopt_solve_failure
(Ipopt::ApplicationReturnStatus const status)
{
DEB_enter_func
DEB_error(" IPOPT solve failure code is " << status)
// TODO: we could translate these return codes, but will not do it for now
// enum ApplicationReturnStatus
// {
// Solve_Succeeded=0,
// Solved_To_Acceptable_Level=1,
// Infeasible_Problem_Detected=2,
// Search_Direction_Becomes_Too_Small=3,
// Diverging_Iterates=4,
// User_Requested_Stop=5,
// Feasible_Point_Found=6,
//
// Maximum_Iterations_Exceeded=-1,
// Restoration_Failed=-2,
// Error_In_Step_Computation=-3,
// Maximum_CpuTime_Exceeded=-4,
// Not_Enough_Degrees_Of_Freedom=-10,
// Invalid_Problem_Definition=-11,
// Invalid_Option=-12,
// Invalid_Number_Detected=-13,
//
// Unrecoverable_Exception=-100,
// NonIpopt_Exception_Thrown=-101,
// Insufficient_Memory=-102,
// Internal_Error=-199
// };
//------------------------------------------------------
switch (status)
{
case Ipopt::Maximum_Iterations_Exceeded:
COMISO_THROW(IPOPT_MAXIMUM_ITERATIONS_EXCEEDED);
case Ipopt::NonIpopt_Exception_Thrown:
// this could be due to a thrown PROGRESS_ABORTED exception, ...
PROGRESS_RESUME_ABORT; // ... so check if we need to resume it
default:
COMISO_THROW(IPOPT_OPTIMIZATION_FAILED);
}
}
static void
check_ipopt_status
(Ipopt::ApplicationReturnStatus const _stat)
{
if (_stat != Ipopt::Solve_Succeeded &&
_stat != Ipopt::Solved_To_Acceptable_Level)
throw_ipopt_solve_failure(_stat);
}
void
IPOPTSolverLean::
solve
(NProblemInterface* _problem,
const std::vector<NConstraintInterface*>& _constraints)
{
DEB_time_func_def;
//----------------------------------------------------------------------------
// 1. Create an instance of IPOPT NLP
//----------------------------------------------------------------------------
Ipopt::SmartPtr<Ipopt::TNLP> np = new IPOPTProblemInstance(_problem, _constraints);
IPOPTProblemInstance* np2 = dynamic_cast<IPOPTProblemInstance*> (Ipopt::GetRawPtr(np));
np2->set_callback_function(impl_->intermediate_callback_);
//----------------------------------------------------------------------------
// 2. exploit special characteristics of problem
//----------------------------------------------------------------------------
DEB_out(2,"exploit detected special properties: ");
if (np2->hessian_constant())
{
DEB_out(2,"*constant hessian* ");
impl_->app_->Options()->SetStringValue("hessian_constant", "yes");
}
if (np2->jac_c_constant())
{
DEB_out(2, "*constant jacobian of equality constraints* ");
impl_->app_->Options()->SetStringValue("jac_c_constant", "yes");
}
if (np2->jac_d_constant())
{
DEB_out(2, "*constant jacobian of in-equality constraints*");
impl_->app_->Options()->SetStringValue("jac_d_constant", "yes");
}
DEB_out(2,"\n");
//----------------------------------------------------------------------------
// 3. solve problem
//----------------------------------------------------------------------------
// Initialize the IpoptApplication and process the options
Ipopt::ApplicationReturnStatus status = impl_->app_->Initialize();
if (status != Ipopt::Solve_Succeeded)
COMISO_THROW(IPOPT_INITIALIZATION_FAILED);
status = impl_->app_->OptimizeTNLP( np);
//----------------------------------------------------------------------------
// 4. output statistics
//----------------------------------------------------------------------------
check_ipopt_status(status);
// Retrieve some statistics about the solve
Ipopt::Index iter_count = impl_->app_->Statistics()->IterationCount();
DEB_out(1,"\n*** IPOPT: The problem solved in "
<< iter_count << " iterations!\n");
Ipopt::Number final_obj = impl_->app_->Statistics()->FinalObjective();
DEB_out(1,"\n*** IPOPT: The final value of the objective function is "