// $Id: maximizer.h,v 1.37 2018/01/03 21:33:02 mkkuhner Exp $

/*
Copyright 2002-2005 Mary K. Kuhner, Peter Beerli, and Joseph Felsenstein

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

/* Authors: Peter Beerli, Mary Kuhner, Jon Yamato and Joseph Felsenstein */


// Maximizer Class ----------------------------------------------
//
//   Maximizer for likelihood function (see PostLike::Calculate())
//   using the Broyden-Fletcher-Shanno-Goldfarb method
//   other methods (e.g. Newton-Raphson) can be easily incorporated
//   by adding another Maximizer::Calculate()
//
//
//   calculates the maximum of the likelihood function and returns
//   the parameters as a vector  at the ML.
//
//   Call sequence and existence through run
//      foreach(locus)
//      {
//         likelihood = SinglePostLike(forces)
//         Maximizer(likelihood)
//         foreach(replicate) [independent replicates, if any]
//         {
//             foreach(single_chains)
//                Calculate(data) uses data and MLE
//             foreach(long_chain)
//                Calculate(data)
//          }
//         ~Maximizer()
//         likelihood = ReplicatePostLike(forces)
//         Maximizer(likelihood)
//         ~Maximizer()
//      }
//      if(locus>1)
//      {
//         likelihood = RegionPostLike(forces)
//         Maximizer(likelihood)
//         ~Maximizer()
//         likelihood = GammaRegionPostLike(forces)
//         Maximizer(likelihood)
//         ~Maximizer()
//       }
//   STILL TODO
//     Line() is not yet implemented
//

#ifndef MAXIMIZER_H
#define MAXIMIZER_H

#include <algorithm>
#include <cmath>
#include <functional>
#include <iostream>
#include <numeric>
#include <vector>

#include "definitions.h"
#include "vectorx.h"
#include "likelihood.h"
#include "runreport.h"
#include "paramstat.h"

using
std::vector;

// declarations --------------------------------------------

class
Maximizer
{
  public:
    Maximizer (long int thisNparam);
    ~Maximizer ();

    void Initialize(long int thisNparam);
    void   SetLikelihood (PostLike * thispostlike);
    bool   Calculate (DoubleVec1d & thisparam, double& lnL, string& message);
    PostLike *GetPostLike()
    {
        return m_pPostLike;             // Analyzer and others need access to this.
    };
    double GetLastNorm();                // Debugging function.
    void   SetMLEs(DoubleVec1d newMLEs); // Need for sumfile reading.

    // Interface for performing constrained maximization--
    // e.g., maximization under the constraint that forward and backward
    // migration rates are equal (M12 == M21).
    bool SetConstraints(const vector<vector<unsigned long int> > & constraints);
    void ClearConstraints(void) { m_constraints.clear(); };
    void AppendConstraintOnAlpha(ParamStatus alphaStatus); // Append to the gradient guide.

    // Gradient guide interface, moved here from the PostLike class,
    // because it's better for the maximizer to control it.
    // (The gradient guide still is, and will be, used by PostLike.)
    void GradientGuideSetup (const ParamVector &thisToDolist, PostLike* pPL);
    void ProfileGuideRestore();
    void ProfileGuideFix(long int guide);
    void ProfileGuideFixAll();
    void ProfileGuideRestore(long int guide);
    likelihoodtype GetPostlikeTag() { return m_pPostLike->GetTag(); };

  private:
    Maximizer ()
    {
    };
    double m_lastnormg;

  protected:
    PostLike *m_pPostLike;        // pointer to PostLike object
    unsigned long int m_nparam;   // number of parameters (m_param.size())
    long int m_nloci;             // number of loci
    long int m_nrep;              // number of replicates
    DoubleVec1d  m_param;         // parameters
    DoubleVec1d  m_lparam;        // log parameters
    DoubleVec1d  m_oldlparam;     // old log parameters
    DoubleVec1d  m_gradient;      // gradient
    DoubleVec1d  m_oldgradient;   // old gradient
    DoubleVec1d  m_paramdelta;    // m_newparam - m_oldparam
    DoubleVec1d  m_gradientdelta; // m_gradient - m_oldgradient
    DoubleVec1d  m_direction;     // direction
    vector<DoubleVec1d> m_second; // approx second derivative

    vector<unsigned long int> m_isLinearParam; // track which parameters get treated
    // logarithmically and which linearly
    bool m_dataHasLinearParam;
    bool m_dataHasGamma;
    bool m_constraintsVectorHasSlotForAlpha;
    double m_maxAllowedValueForAlpha;

    // MARY  -- temporary storage, kept as class variables for speed
    DoubleVec1d m_newparam;
    DoubleVec1d m_newlparam;
    DoubleVec1d m_minparamvalues;
    DoubleVec1d m_minlparamvalues;
    DoubleVec1d m_maxparamvalues;
    DoubleVec1d m_maxlparamvalues;
    DoubleVec1d m_temp;
    DoubleVec2d m_dd;

    vector<vector<unsigned long int> > m_constraints; // user can constrain params
    // to equal one another
    bool m_constrained; // assume this is faster than evaluating m_constraints.empty()
    DoubleVec1d m_constraintratio; // for multiplicative constraints

    double Norm (const DoubleVec1d&d); // utility function: norm of vector
    // (i.e., its length)

    bool CalculateSteepest(double&, string& message);
    bool CalculateByParts(double&, string& message);
    bool CalculateBroyden(unsigned long int maxtrials, double &, string & message);

    // calculates the new parameter
    double SetParam(const double &lambda, const DoubleVec1d& direction);
    bool SetParam1d(const double& step, const vector<unsigned long int> * pWhichparam);

    void CoutNewParams() const; // for debugging
    void CoutCurParams() const; // for debugging
    void CoutByLinOrNoMult(double mult, const DoubleVec1d&) const; // for debugging

    // Used by CalculateSteepest().
    bool BracketTheMaximumAndFindIt(const vector<unsigned long int>
                                    * pWhichparam, const double epsilon,
                                    double oldlike, double & newlike,
                                    string & message);

    // Handles constrained parameters when applicable.
    bool DCalculate(const DoubleVec1d & param, DoubleVec1d & gradient);

    void ResetSecond ();          //resets the second derivative matrix
    void CalcSecond ();           // calculate approximative second derivatives
    void CalcDirection ();        // calculate the direction of change

    void SetGradGuide(const vector<ParamStatus>& thisguide)
    {
        std::copy(thisguide.begin(), thisguide.end(),
                  m_pPostLike->m_working_pstatusguides.begin());
    };

    void ExplainParamChange(long index, double oldVal, double newVal, std::string kind);
    void AdjustExtremeLinearParam(long i);
    void AdjustExtremeLogParam(long i);
};

// helper functions

// simply returns result = one - two
void CalcDelta (const DoubleVec1d & one, const DoubleVec1d & two, DoubleVec1d & result);

inline bool SlopesHaveSameSign(const double & oldgrad, const double & newgrad, const double & tolerance);
inline double sign(const double & x);
inline double tolerance(double number);

#endif // MAXIMIZER_H

//____________________________________________________________________________________
