/*  _______________________________________________________________________

    Dakota: Explore and predict with confidence.
    Copyright 2014-2023
    National Technology & Engineering Solutions of Sandia, LLC (NTESS).
    This software is distributed under the GNU Lesser General Public License.
    For more information, see the README file in the top Dakota directory.
    _______________________________________________________________________ */

#ifndef COLIN_OPTIMIZER_H
#define COLIN_OPTIMIZER_H

#include "DakotaOptimizer.hpp"

#include <colin/SolverMngr.h>
#include <colin/ApplicationMngr.h>

// forward declarations
class COLINApplication;

namespace Dakota {

/**
 * \brief A version of TraitsBase specialized for COLIN optimizers
 *
 */
class COLINTraits: public TraitsBase
{
  public:

  /// default constructor
  COLINTraits() { }

  /// destructor
  virtual ~COLINTraits() { }

  /// A temporary query used in the refactor
  virtual bool is_derived() { return true; }

  // Traits are chosen to be the most common ones across a majority of methods within this TPL.

  /// Return the flag indicating whether method supports continuous variables
  bool supports_continuous_variables() { return true; }

  /// Return the flag indicating whether method supports nonlinear equalities
  bool supports_nonlinear_equality() { return true; }

  /// Return the flag indicating whether method supports nonlinear inequalities
  bool supports_nonlinear_inequality() { return true; }

  /// Return the format used for nonlinear inequality constraints
  NONLINEAR_INEQUALITY_FORMAT nonlinear_inequality_format()
    { return NONLINEAR_INEQUALITY_FORMAT::TWO_SIDED; }

};


/// Wrapper class for optimizers defined using COLIN 

/** The COLINOptimizer class wraps COLIN, a Sandia-developed C++
    optimization interface library.  A variety of COLIN optimizers are
    defined in COLIN and its associated libraries, including SCOLIB
    which contains the optimization components from the old COLINY
    (formerly SGOPT) library. COLIN contains optimizers such as
    genetic algorithms, pattern search methods, and other
    nongradient-based techniques. COLINOptimizer uses a
    COLINApplication object to perform the function evaluations.

    The user input mappings are as follows: \c max_iterations, \c
    max_function_evaluations, \c convergence_tolerance, and \c
    solution_accuracy are mapped into COLIN's \c max_iterations, \c
    max_function_evaluations_this_trial, \c function_value_tolerance,
    \c sufficient_objective_value properties.  An \c outputLevel is
    mapped to COLIN's \c output_level property and a setting of \c
    debug activates output of method initialization and sets the COLIN
    \c debug attribute to 10000 for the DEBUG output level. Refer to
    [Hart, W.E., 2006] for additional information on COLIN objects and
    controls. */
class COLINOptimizer : public Optimizer
{
public:

  //
  //- Heading: Constructors and destructor
  //

  /// standard constructor
  COLINOptimizer(ProblemDescDB& problem_db, Model& model);
  /// alternate constructor for on-the-fly instantiations
  COLINOptimizer(const String& method_name, Model& model, int seed,
		 size_t max_iter, size_t max_eval);
  /// alternate constructor for Iterator instantiations by name
  COLINOptimizer(const String& method_name, Model& model);
  /// destructor
  ~COLINOptimizer() {
    if (rng) delete rng;
    //colin::CacheFactory().unregister_cache("useThisCache");
    //String unique_cache_name(method_id());
    //unique_cache_name += methodName;
    //colin::CacheFactory().unregister_cache(unique_cache_name);
  }

  //
  //- Heading: Virtual member function redefinitions
  //

  /// clears internal optimizer state
  void reset();

  /// iterates the COLIN solver to determine the optimal solution
  void core_run();

  // COLIN methods cannot yet accept multiple initial points
  //bool accepts_multiple_points() const;

  /// some COLIN methods can return multiple points
  bool returns_multiple_points() const;

protected:

  //
  //- Heading: constructor convenience member functions
  //
  
  /// convenience function for setting up the particular COLIN solver
  /// and appropriate Application
  void solver_setup(unsigned short method_name);

  /// sets up the random number generator for stochastic methods
  void set_rng(int seed);

  /// sets construct-time options for specific methods based on user
  /// specifications, including calling method-specific set functions
  void set_solver_parameters();

  //
  //- Heading: runtime convenience member functions
  //

  /// Get the final set of points from the solver
  /// Look up responses and sort, first according
  /// to constraint violation, then according to
  /// function value
  void post_run(std::ostream& s);

  /// Retrieve response from Colin AppResponse, return pair indicating
  /// success for <objective, constraints>
  std::pair<bool, bool> 
  colin_cache_lookup(const colin::AppResponse& colinResponse,
		     Response& tmpResponseHolder);
  
  /// Compute constraint violation, based on nonlinear constraints in
  /// iteratedModel and provided Response data
  double constraint_violation(const Response& tmpResponseHolder);

  //
  //- Heading: Data
  //

  /// COLIN solver sub-type as enumerated in COLINOptimizer.cpp
  short solverType;

  /// handle to the COLIN solver
  colin::SolverHandle colinSolver;

  /// handle and pointer to the COLINApplication object
  std::pair<colin::ApplicationHandle, COLINApplication*> colinProblem;

  /// pointer to the COLIN evalutaion manager object
  colin::EvaluationManager_Base* colinEvalMgr;

  /// random number generator pointer
  utilib::RNG* rng;
  
  /// the \c synchronization setting: true if \c blocking, false if
  /// \c nonblocking
  bool blockingSynch;

  /// Buffer to hold problem constraint_penalty parameter
  Real constraint_penalty;

  /// Buffer to hold problem constant_penalty parameter
  bool constant_penalty;
};


inline void COLINOptimizer::reset()
{ colinSolver->reset(); }

} // namespace Dakota

#endif
