/* $Id: conv-exprLog.cpp 490 2011-01-14 16:07:12Z pbelotti $
 *
 * Name:    conv-exprLog.cpp
 * Author:  Pietro Belotti
 * Purpose: convexification and bounding methods for the logarithm operator
 *
 * (C) Carnegie-Mellon University, 2006-09.
 * This file is licensed under the Eclipse Public License (EPL)
 */

#include "CouenneCutGenerator.hpp"

#include "CouenneTypes.hpp"
#include "CouenneExprLog.hpp"
#include "CouenneExprPow.hpp"
#include "CouenneExprAux.hpp"

#include "CouenneProblem.hpp"

using namespace Couenne;

#define LOG_STEP 10
#define LOG_MININF 1e-50

// generate convexification cut for constraint w = this

void exprLog::generateCuts (expression *aux, //const OsiSolverInterface &si, 
			    OsiCuts &cs, const CouenneCutGenerator *cg,
			    t_chg_bounds *chg, int wind, 
			    CouNumber lbw, CouNumber ubw) {
  int 
    w_ind = aux       -> Index (), //   dependent variable's index
    x_ind = argument_ -> Index (); // independent variable's index

  bool changed = 
    !chg || (cg -> isFirst ()) || 
    (chg [x_ind].lower() != t_chg_bounds::UNCHANGED) ||
    (chg [x_ind].upper() != t_chg_bounds::UNCHANGED);

  CouNumber l, u;
  argument_ -> getBounds (l, u);

  enum auxSign sign = cg -> Problem () -> Var (w_ind) -> sign ();

  // if bounds are very close, convexify with a single line
  if ((fabs (u - l) < COUENNE_EPS) && (l > COUENNE_EPS)) {

    CouNumber x0 = 0.5 * (u+l);
    if (changed) cg -> createCut (cs, log (x0) - 1, sign, w_ind, 1., x_ind, - 1/x0);
    return;
  }

  // fix lower bound
  if (l < LOG_MININF) l = LOG_MININF;
  else   // lower segment (if l is far from zero and u finite)
    if ((u < COUENNE_INFINITY) && changed && sign != expression::AUX_LEQ) { 

      CouNumber dx   = u-l;
      CouNumber logu = log (u);
      CouNumber dw   = logu - log (l);

      cg -> createCut (cs, dx*logu - u*dw, +1, w_ind, dx, x_ind, -dw);
    }

  // no need to continue if auxiliary is defined as w >= log (x)
  if (sign == expression::AUX_GEQ) 
    return;

  // pick tangent point closest to current point (x0,y0)
  CouNumber x = (cg -> isFirst ()) ? 
                 1 : powNewton ((*argument_) (), (*aux) (), log, inv, oppInvSqr);

  // check if outside interval
  if      (x < l) x = l;
  else if (x > u) x = u;

  // fix bound interval (unless you like infinite coefficients)
  if (u > 1e5 * log (COUENNE_INFINITY) - 1)
    u = x + (LOG_STEP << cg -> nSamples ());

  // add upper envelope
  cg -> addEnvelope (cs, -1, log, inv, w_ind, x_ind, x, l, u, chg, true);
}
