// Copyright (C) 2004, 2006 International Business Machines and others.
// All Rights Reserved.
// This code is published under the Common Public License.
//
// $Id: IpNLPScaling.hpp 759 2006-07-07 03:07:08Z andreasw $
//
// Authors:  Carl Laird, Andreas Waechter     IBM    2004-08-13

#ifndef __IPNLPSCALING_HPP__
#define __IPNLPSCALING_HPP__

#include "IpSymMatrix.hpp"
#include "IpScaledMatrix.hpp"
#include "IpSymScaledMatrix.hpp"
#include "IpOptionsList.hpp"
#include "IpRegOptions.hpp"

namespace SimTKIpopt
{
  /** This is the abstract base class for problem scaling.
   *  It is repsonsible for determining the scaling factors
   *  and mapping quantities in and out of scaled and unscaled
   *  versions 
   */
  class NLPScalingObject : public ReferencedObject
  {
  public:
    /**@name Constructors/Destructors */
    //@{
    NLPScalingObject()
    {}

    /** Default destructor */
    virtual ~NLPScalingObject()
    {}
    //@}

    /** Method to initialize the options */
    bool Initialize(const Journalist& jnlst,
                    const OptionsList& options,
                    const std::string& prefix)
    {
      jnlst_ = &jnlst;
      return InitializeImpl(options, prefix);
    }

    /** Methods to map scaled and unscaled matrices */
    //@{
    /** Returns an obj-scaled version of the given scalar */
    virtual Number apply_obj_scaling(const Number& f)=0;
    /** Returns an obj-unscaled version of the given scalar */
    virtual Number unapply_obj_scaling(const Number& f)=0;
    /** Returns an x-scaled version of the given vector */
    virtual SmartPtr<Vector>
    apply_vector_scaling_x_NonConst(const SmartPtr<const Vector>& v)=0;
    /** Returns an x-scaled version of the given vector */
    virtual SmartPtr<const Vector>
    apply_vector_scaling_x(const SmartPtr<const Vector>& v)=0;
    /** Returns an x-unscaled version of the given vector */
    virtual SmartPtr<Vector>
    unapply_vector_scaling_x_NonConst(const SmartPtr<const Vector>& v)=0;
    /** Returns an x-unscaled version of the given vector */
    virtual SmartPtr<const Vector>
    unapply_vector_scaling_x(const SmartPtr<const Vector>& v)=0;
    /** Returns an c-scaled version of the given vector */
    virtual SmartPtr<const Vector>
    apply_vector_scaling_c(const SmartPtr<const Vector>& v)=0;
    /** Returns an c-unscaled version of the given vector */
    virtual SmartPtr<const Vector>
    unapply_vector_scaling_c(const SmartPtr<const Vector>& v)=0;
    /** Returns an c-scaled version of the given vector */
    virtual SmartPtr<Vector>
    apply_vector_scaling_c_NonConst(const SmartPtr<const Vector>& v)=0;
    /** Returns an c-unscaled version of the given vector */
    virtual SmartPtr<Vector>
    unapply_vector_scaling_c_NonConst(const SmartPtr<const Vector>& v)=0;
    /** Returns an d-scaled version of the given vector */
    virtual SmartPtr<const Vector>
    apply_vector_scaling_d(const SmartPtr<const Vector>& v)=0;
    /** Returns an d-unscaled version of the given vector */
    virtual SmartPtr<const Vector>
    unapply_vector_scaling_d(const SmartPtr<const Vector>& v)=0;
    /** Returns an d-scaled version of the given vector */
    virtual SmartPtr<Vector>
    apply_vector_scaling_d_NonConst(const SmartPtr<const Vector>& v)=0;
    /** Returns an d-unscaled version of the given vector */
    virtual SmartPtr<Vector>
    unapply_vector_scaling_d_NonConst(const SmartPtr<const Vector>& v)=0;
    /** Returns a scaled version of the jacobian for c.  If the
     *  overloaded method does not make a new matrix, make sure to set
     *  the matrix ptr passed in to NULL.
     */
    virtual SmartPtr<const Matrix>
    apply_jac_c_scaling(SmartPtr<const Matrix> matrix)=0;
    /** Returns a scaled version of the jacobian for d If the
     *  overloaded method does not create a new matrix, make sure to
     *  set the matrix ptr passed in to NULL.
     */
    virtual SmartPtr<const Matrix>
    apply_jac_d_scaling(SmartPtr<const Matrix> matrix)=0;
    /** Returns a scaled version of the hessian of the lagrangian If
     *  the overloaded method does not create a new matrix, make sure
     *  to set the matrix ptr passed in to NULL.
     */
    virtual SmartPtr<const SymMatrix>
    apply_hessian_scaling(SmartPtr<const SymMatrix> matrix)=0;
    //@}

    /** Methods for scaling bounds - these wrap those above */
    //@{
    /** Returns an x-scaled vector in the x_L or x_U space */
    SmartPtr<Vector> apply_vector_scaling_x_LU_NonConst(
      const Matrix& Px_LU,
      const SmartPtr<const Vector>& lu,
      const VectorSpace& x_space);
    /** Returns an x-scaled vector in the x_L or x_U space */
    SmartPtr<const Vector> apply_vector_scaling_x_LU(
      const Matrix& Px_LU,
      const SmartPtr<const Vector>& lu,
      const VectorSpace& x_space);
    /** Returns an d-scaled vector in the d_L or d_U space */
    SmartPtr<Vector> apply_vector_scaling_d_LU_NonConst(
      const Matrix& Pd_LU,
      const SmartPtr<const Vector>& lu,
      const VectorSpace& d_space);
    /** Returns an d-scaled vector in the d_L or d_U space */
    SmartPtr<const Vector> apply_vector_scaling_d_LU(
      const Matrix& Pd_LU,
      const SmartPtr<const Vector>& lu,
      const VectorSpace& d_space);
    /** Returns an d-unscaled vector in the d_L or d_U space */
    SmartPtr<Vector> unapply_vector_scaling_d_LU_NonConst(
      const Matrix& Pd_LU,
      const SmartPtr<const Vector>& lu,
      const VectorSpace& d_space);
    /** Returns an d-unscaled vector in the d_L or d_U space */
    SmartPtr<const Vector> unapply_vector_scaling_d_LU(
      const Matrix& Pd_LU,
      const SmartPtr<const Vector>& lu,
      const VectorSpace& d_space);
    //@}

    /** Methods for scaling the gradient of the objective - wraps the
     *  virtual methods above
     */
    //@{
    /** Returns a grad_f scaled version (d_f * D_x^{-1}) of the given vector */
    virtual SmartPtr<Vector>
    apply_grad_obj_scaling_NonConst(const SmartPtr<const Vector>& v);
    /** Returns a grad_f scaled version (d_f * D_x^{-1}) of the given vector */
    virtual SmartPtr<const Vector>
    apply_grad_obj_scaling(const SmartPtr<const Vector>& v);
    /** Returns a grad_f unscaled version (d_f * D_x^{-1}) of the
     *  given vector */
    virtual SmartPtr<Vector>
    unapply_grad_obj_scaling_NonConst(const SmartPtr<const Vector>& v);
    /** Returns a grad_f unscaled version (d_f * D_x^{-1}) of the
     *  given vector */
    virtual SmartPtr<const Vector>
    unapply_grad_obj_scaling(const SmartPtr<const Vector>& v);
    //@}

    /** @name Methods for determining whether scaling for entities is
     *  done */
    //@{
    /** Returns true if the primal x variables are scaled. */
    virtual bool have_x_scaling()=0;
    /** Returns true if the equality constraints are scaled. */
    virtual bool have_c_scaling()=0;
    /** Returns true if the inequality constraints are scaled. */
    virtual bool have_d_scaling()=0;
    //@}

    /** This method is called by the IpoptNLP's at a convenient time to
     *  compute and/or read scaling factors 
     */
    virtual void DetermineScaling(const SmartPtr<const VectorSpace> x_space,
                                  const SmartPtr<const VectorSpace> c_space,
                                  const SmartPtr<const VectorSpace> d_space,
                                  const SmartPtr<const MatrixSpace> jac_c_space,
                                  const SmartPtr<const MatrixSpace> jac_d_space,
                                  const SmartPtr<const SymMatrixSpace> h_space,
                                  SmartPtr<const MatrixSpace>& new_jac_c_space,
                                  SmartPtr<const MatrixSpace>& new_jac_d_space,
                                  SmartPtr<const SymMatrixSpace>& new_h_space)=0;
  protected:
    /** Implementation of the initialization method that has to be
     *  overloaded by for each derived class. */
    virtual bool InitializeImpl(const OptionsList& options,
                                const std::string& prefix)=0;

    /** Accessor method for the journalist */
    const Journalist& Jnlst() const
    {
      return *jnlst_;
    }
  private:

    /**@name Default Compiler Generated Methods
     * (Hidden to avoid implicit creation/calling).
     * These methods are not implemented and 
     * we do not want the compiler to implement
     * them for us, so we declare them private
     * and do not define them. This ensures that
     * they will not be implicitly created/called. */
    //@{

    /** Copy Constructor */
    NLPScalingObject(const NLPScalingObject&);

    /** Overloaded Equals Operator */
    void operator=(const NLPScalingObject&);
    //@}

    SmartPtr<const Journalist> jnlst_;
  };

  /** This is a base class for many standard scaling
   *  techniques. The overloaded classes only need to
   *  provide the scaling parameters
   */
  class StandardScalingBase : public NLPScalingObject
  {
  public:
    /**@name Constructors/Destructors */
    //@{
    StandardScalingBase()
    {}

    /** Default destructor */
    virtual ~StandardScalingBase()
    {}
    //@}

    /** Methods to map scaled and unscaled matrices */
    //@{
    /** Returns an obj-scaled version of the given scalar */
    virtual Number apply_obj_scaling(const Number& f) override;
    /** Returns an obj-unscaled version of the given scalar */
    virtual Number unapply_obj_scaling(const Number& f) override;
    /** Returns an x-scaled version of the given vector */
    virtual SmartPtr<Vector>
    apply_vector_scaling_x_NonConst(const SmartPtr<const Vector>& v) override;
    /** Returns an x-scaled version of the given vector */
    virtual SmartPtr<const Vector>
    apply_vector_scaling_x(const SmartPtr<const Vector>& v) override;
    /** Returns an x-unscaled version of the given vector */
    virtual SmartPtr<Vector>
    unapply_vector_scaling_x_NonConst(const SmartPtr<const Vector>& v) override;
    /** Returns an x-unscaled version of the given vector */
    virtual SmartPtr<const Vector>
    unapply_vector_scaling_x(const SmartPtr<const Vector>& v) override;
    /** Returns an c-scaled version of the given vector */
    virtual SmartPtr<const Vector>
    apply_vector_scaling_c(const SmartPtr<const Vector>& v) override;
    /** Returns an c-unscaled version of the given vector */
    virtual SmartPtr<const Vector>
    unapply_vector_scaling_c(const SmartPtr<const Vector>& v) override;
    /** Returns an c-scaled version of the given vector */
    virtual SmartPtr<Vector>
    apply_vector_scaling_c_NonConst(const SmartPtr<const Vector>& v) override;
    /** Returns an c-unscaled version of the given vector */
    virtual SmartPtr<Vector>
    unapply_vector_scaling_c_NonConst(const SmartPtr<const Vector>& v) override;
    /** Returns an d-scaled version of the given vector */
    virtual SmartPtr<const Vector>
    apply_vector_scaling_d(const SmartPtr<const Vector>& v) override;
    /** Returns an d-unscaled version of the given vector */
    virtual SmartPtr<const Vector>
    unapply_vector_scaling_d(const SmartPtr<const Vector>& v) override;
    /** Returns an d-scaled version of the given vector */
    virtual SmartPtr<Vector>
    apply_vector_scaling_d_NonConst(const SmartPtr<const Vector>& v) override;
    /** Returns an d-unscaled version of the given vector */
    virtual SmartPtr<Vector>
    unapply_vector_scaling_d_NonConst(const SmartPtr<const Vector>& v) override;
    /** Returns a scaled version of the jacobian for c.  If the
     *  overloaded method does not make a new matrix, make sure to set
     *  the matrix ptr passed in to NULL.
     */
    virtual SmartPtr<const Matrix>
    apply_jac_c_scaling(SmartPtr<const Matrix> matrix) override;
    /** Returns a scaled version of the jacobian for d If the
     *  overloaded method does not create a new matrix, make sure to
     *  set the matrix ptr passed in to NULL.
     */
    virtual SmartPtr<const Matrix>
    apply_jac_d_scaling(SmartPtr<const Matrix> matrix) override;
    /** Returns a scaled version of the hessian of the lagrangian If
     *  the overloaded method does not create a new matrix, make sure
     *  to set the matrix ptr passed in to NULL.
     */
    virtual SmartPtr<const SymMatrix>
    apply_hessian_scaling(SmartPtr<const SymMatrix> matrix) override;
    //@}

    /** @name Methods for determining whether scaling for entities is
     *  done */
    //@{
    virtual bool have_x_scaling() override;
    virtual bool have_c_scaling() override;
    virtual bool have_d_scaling() override;
    //@}

    /** This method is called by the IpoptNLP's at a convenient time to
     *  compute and/or read scaling factors 
     */
    virtual void DetermineScaling(const SmartPtr<const VectorSpace> x_space,
                                  const SmartPtr<const VectorSpace> c_space,
                                  const SmartPtr<const VectorSpace> d_space,
                                  const SmartPtr<const MatrixSpace> jac_c_space,
                                  const SmartPtr<const MatrixSpace> jac_d_space,
                                  const SmartPtr<const SymMatrixSpace> h_space,
                                  SmartPtr<const MatrixSpace>& new_jac_c_space,
                                  SmartPtr<const MatrixSpace>& new_jac_d_space,
                                  SmartPtr<const SymMatrixSpace>& new_h_space) override;

    /** Methods for IpoptType */
    //@{
    static void RegisterOptions(SmartPtr<RegisteredOptions> roptions);
    //@}

  protected:
    /** Overloaded initialization method */
    virtual bool InitializeImpl(const OptionsList& options,
                                const std::string& prefix) override;

    /** This is the method that has to be overloaded by a particular
     *  scaling method that somehow computes the scaling vectors dx,
     *  dc, and dd.  The pointers to those vectors can be NULL, in
     *  which case no scaling for that item will be done later. */
    virtual void DetermineScalingParametersImpl(
      const SmartPtr<const VectorSpace> x_space,
      const SmartPtr<const VectorSpace> c_space,
      const SmartPtr<const VectorSpace> d_space,
      const SmartPtr<const MatrixSpace> jac_c_space,
      const SmartPtr<const MatrixSpace> jac_d_space,
      const SmartPtr<const SymMatrixSpace> h_space,
      Number& df,
      SmartPtr<Vector>& dx,
      SmartPtr<Vector>& dc,
      SmartPtr<Vector>& dd)=0;

  private:

    /**@name Default Compiler Generated Methods
     * (Hidden to avoid implicit creation/calling).
     * These methods are not implemented and 
     * we do not want the compiler to implement
     * them for us, so we declare them private
     * and do not define them. This ensures that
     * they will not be implicitly created/called. */
    //@{

    /** Copy Constructor */
    StandardScalingBase(const StandardScalingBase&);

    /** Overloaded Equals Operator */
    void operator=(const StandardScalingBase&);
    //@}

    /** Scaling parameters - we only need to keep copies of
     *  the objective scaling and the x scaling - the others we can
     *  get from the scaled matrix spaces.
     */
    //@{
    /** objective scaling parameter */
    Number df_;
    /** x scaling */
    SmartPtr<Vector> dx_;
    //@}

    /** Scaled Matrix Spaces */
    //@{
    /** Scaled jacobian of c space */
    SmartPtr<ScaledMatrixSpace> scaled_jac_c_space_;
    /** Scaled jacobian of d space */
    SmartPtr<ScaledMatrixSpace> scaled_jac_d_space_;
    /** Scaled hessian of lagrangian spacea */
    SmartPtr<SymScaledMatrixSpace> scaled_h_space_;
    //@}

    /** @name Algorithmic parameters */
    //@{
    /** Additional scaling value for the objective function */
    Number obj_scaling_factor_;
    //@}
  };

  /** Class implementing the scaling object that doesn't to any scaling */
  class NoNLPScalingObject : public StandardScalingBase
  {
  public:
    /**@name Constructors/Destructors */
    //@{
    NoNLPScalingObject()
    {}

    /** Default destructor */
    virtual ~NoNLPScalingObject()
    {}
    //@}


  protected:
    /** Overloaded from StandardScalingBase */
    virtual void DetermineScalingParametersImpl(
      const SmartPtr<const VectorSpace> x_space,
      const SmartPtr<const VectorSpace> c_space,
      const SmartPtr<const VectorSpace> d_space,
      const SmartPtr<const MatrixSpace> jac_c_space,
      const SmartPtr<const MatrixSpace> jac_d_space,
      const SmartPtr<const SymMatrixSpace> h_space,
      Number& df,
      SmartPtr<Vector>& dx,
      SmartPtr<Vector>& dc,
      SmartPtr<Vector>& dd) override;

  private:

    /**@name Default Compiler Generated Methods
     * (Hidden to avoid implicit creation/calling).
     * These methods are not implemented and 
     * we do not want the compiler to implement
     * them for us, so we declare them private
     * and do not define them. This ensures that
     * they will not be implicitly created/called. */
    //@{

    /** Copy Constructor */
    NoNLPScalingObject(const NoNLPScalingObject&);

    /** Overloaded Equals Operator */
    void operator=(const NoNLPScalingObject&);
    //@}
  };

} // namespace Ipopt

#endif
