private boolean getNextPosition(
     final Function1D<DoubleMatrix1D, Double> function,
     final Function1D<DoubleMatrix1D, DoubleMatrix1D> grad,
     final DataBundle data) {
   final DoubleMatrix1D p = getDirection(data);
   if (data.getLambda0() < 1.0) {
     data.setLambda0(1.0);
   } else {
     data.setLambda0(data.getLambda0() * BETA);
   }
   updatePosition(p, function, data);
   final double g1 = data.getG1();
   // the function is invalid at the new position, try to recover
   if (Double.isInfinite(g1) || Double.isNaN(g1)) {
     bisectBacktrack(p, function, data);
   }
   if (data.getG1() > data.getG0() / (1 + ALPHA * data.getLambda0())) {
     quadraticBacktrack(p, function, data);
     int count = 0;
     while (data.getG1() > data.getG0() / (1 + ALPHA * data.getLambda0())) {
       if (count > 5) {
         return false;
       }
       cubicBacktrack(p, function, data);
       count++;
     }
   }
   final DoubleMatrix1D deltaX = data.getDeltaX();
   data.setX((DoubleMatrix1D) MA.add(data.getX(), deltaX));
   data.setG0(data.getG1());
   final DoubleMatrix1D gradNew = grad.evaluate(data.getX());
   data.setDeltaGrad((DoubleMatrix1D) MA.subtract(gradNew, data.getGrad()));
   data.setGrad(gradNew);
   return true;
 }
  @Override
  public DoubleMatrix1D minimize(
      final Function1D<DoubleMatrix1D, Double> function,
      final Function1D<DoubleMatrix1D, DoubleMatrix1D> grad,
      final DoubleMatrix1D startPosition) {
    final DataBundle data = new DataBundle();
    final double y = function.evaluate(startPosition);
    data.setX(startPosition);
    data.setG0(y * y);
    data.setGrad(grad.evaluate(startPosition));
    data.setInverseHessianEsimate(getInitializedMatrix(startPosition));

    if (!getNextPosition(function, grad, data)) {
      throw new MathException(
          "Cannot work with this starting position. Please choose another point");
    }

    int count = 0;
    int resetCount = 1;

    while (!isConverged(data)) {
      if ((resetCount) % RESET_FREQ == 0) {
        data.setInverseHessianEsimate(getInitializedMatrix(startPosition));
        resetCount = 1;
      } else {
        _hessainUpdater.update(data);
      }
      if (!getNextPosition(function, grad, data)) {
        data.setInverseHessianEsimate(getInitializedMatrix(startPosition));
        resetCount = 1;
        if (!getNextPosition(function, grad, data)) {
          throw new MathException("Failed to converge in backtracking");
        }
      }
      count++;
      resetCount++;
      if (count > _maxSteps) {
        throw new MathException(
            "Failed to converge after "
                + _maxSteps
                + " iterations. Final point reached: "
                + data.getX().toString());
      }
    }
    return data.getX();
  }
 protected void updatePosition(
     final DoubleMatrix1D p,
     final Function1D<DoubleMatrix1D, Double> function,
     final DataBundle data) {
   final double lambda0 = data.getLambda0();
   final DoubleMatrix1D deltaX = (DoubleMatrix1D) MA.scale(p, lambda0);
   final DoubleMatrix1D xNew = (DoubleMatrix1D) MA.add(data.getX(), deltaX);
   data.setDeltaX(deltaX);
   data.setG2(data.getG1());
   final double y = function.evaluate(xNew);
   data.setG1(y * y);
 }
 private boolean isConverged(final DataBundle data) {
   final DoubleMatrix1D deltaX = data.getDeltaX();
   final DoubleMatrix1D x = data.getX();
   final int n = deltaX.getNumberOfElements();
   double diff, scale;
   for (int i = 0; i < n; i++) {
     diff = Math.abs(deltaX.getEntry(i));
     scale = Math.abs(x.getEntry(i));
     if (diff > _absoluteTol + scale * _relativeTol) {
       return false;
     }
   }
   return (MA.getNorm2(data.getGrad()) < _absoluteTol);
 }