예제 #1
0
  /** {@inheritDoc} */
  @Override
  protected PointValuePair doOptimize() {
    checkParameters();

    // Indirect call to "computeObjectiveValue" in order to update the
    // evaluations counter.
    final MultivariateFunction evalFunc =
        new MultivariateFunction() {
          public double value(double[] point) {
            return computeObjectiveValue(point);
          }
        };

    final boolean isMinim = getGoalType() == GoalType.MINIMIZE;
    final Comparator<PointValuePair> comparator =
        new Comparator<PointValuePair>() {
          public int compare(final PointValuePair o1, final PointValuePair o2) {
            final double v1 = o1.getValue();
            final double v2 = o2.getValue();
            return isMinim ? Double.compare(v1, v2) : Double.compare(v2, v1);
          }
        };

    // Initialize search.
    simplex.build(getStartPoint());
    simplex.evaluate(evalFunc, comparator);

    PointValuePair[] previous = null;
    int iteration = 0;
    final ConvergenceChecker<PointValuePair> checker = getConvergenceChecker();
    while (true) {
      if (getIterations() > 0) {

        boolean converged = true;
        for (int i = 0; i < simplex.getSize(); i++) {
          PointValuePair prev = previous[i];
          converged = converged && checker.converged(iteration, prev, simplex.getPoint(i));
        }
        if (converged) {
          // We have found an optimum.
          return simplex.getPoint(0);
        }
      }

      // We still need to search.
      previous = simplex.getPoints();
      simplex.iterate(evalFunc, comparator);

      incrementIterationCount();
    }
  }
  /** {@inheritDoc} */
  public Optimum optimize(final LeastSquaresProblem problem) {
    // Pull in relevant data from the problem as locals.
    final int nR = problem.getObservationSize(); // Number of observed data.
    final int nC = problem.getParameterSize(); // Number of parameters.
    // Counters.
    final Incrementor iterationCounter = problem.getIterationCounter();
    final Incrementor evaluationCounter = problem.getEvaluationCounter();
    // Convergence criterion.
    final ConvergenceChecker<Evaluation> checker = problem.getConvergenceChecker();

    // arrays shared with the other private methods
    final int solvedCols = FastMath.min(nR, nC);
    /* Parameters evolution direction associated with lmPar. */
    double[] lmDir = new double[nC];
    /* Levenberg-Marquardt parameter. */
    double lmPar = 0;

    // local point
    double delta = 0;
    double xNorm = 0;
    double[] diag = new double[nC];
    double[] oldX = new double[nC];
    double[] oldRes = new double[nR];
    double[] qtf = new double[nR];
    double[] work1 = new double[nC];
    double[] work2 = new double[nC];
    double[] work3 = new double[nC];

    // Evaluate the function at the starting point and calculate its norm.
    evaluationCounter.incrementCount();
    // value will be reassigned in the loop
    Evaluation current = problem.evaluate(problem.getStart());
    double[] currentResiduals = current.getResiduals().toArray();
    double currentCost = current.getCost();
    double[] currentPoint = current.getPoint().toArray();

    // Outer loop.
    boolean firstIteration = true;
    while (true) {
      iterationCounter.incrementCount();

      final Evaluation previous = current;

      // QR decomposition of the jacobian matrix
      final InternalData internalData = qrDecomposition(current.getJacobian(), solvedCols);
      final double[][] weightedJacobian = internalData.weightedJacobian;
      final int[] permutation = internalData.permutation;
      final double[] diagR = internalData.diagR;
      final double[] jacNorm = internalData.jacNorm;

      // residuals already have weights applied
      double[] weightedResidual = currentResiduals;
      for (int i = 0; i < nR; i++) {
        qtf[i] = weightedResidual[i];
      }

      // compute Qt.res
      qTy(qtf, internalData);

      // now we don't need Q anymore,
      // so let jacobian contain the R matrix with its diagonal elements
      for (int k = 0; k < solvedCols; ++k) {
        int pk = permutation[k];
        weightedJacobian[k][pk] = diagR[pk];
      }

      if (firstIteration) {
        // scale the point according to the norms of the columns
        // of the initial jacobian
        xNorm = 0;
        for (int k = 0; k < nC; ++k) {
          double dk = jacNorm[k];
          if (dk == 0) {
            dk = 1.0;
          }
          double xk = dk * currentPoint[k];
          xNorm += xk * xk;
          diag[k] = dk;
        }
        xNorm = FastMath.sqrt(xNorm);

        // initialize the step bound delta
        delta = (xNorm == 0) ? initialStepBoundFactor : (initialStepBoundFactor * xNorm);
      }

      // check orthogonality between function vector and jacobian columns
      double maxCosine = 0;
      if (currentCost != 0) {
        for (int j = 0; j < solvedCols; ++j) {
          int pj = permutation[j];
          double s = jacNorm[pj];
          if (s != 0) {
            double sum = 0;
            for (int i = 0; i <= j; ++i) {
              sum += weightedJacobian[i][pj] * qtf[i];
            }
            maxCosine = FastMath.max(maxCosine, FastMath.abs(sum) / (s * currentCost));
          }
        }
      }
      if (maxCosine <= orthoTolerance) {
        // Convergence has been reached.
        return new OptimumImpl(current, evaluationCounter.getCount(), iterationCounter.getCount());
      }

      // rescale if necessary
      for (int j = 0; j < nC; ++j) {
        diag[j] = FastMath.max(diag[j], jacNorm[j]);
      }

      // Inner loop.
      for (double ratio = 0; ratio < 1.0e-4; ) {

        // save the state
        for (int j = 0; j < solvedCols; ++j) {
          int pj = permutation[j];
          oldX[pj] = currentPoint[pj];
        }
        final double previousCost = currentCost;
        double[] tmpVec = weightedResidual;
        weightedResidual = oldRes;
        oldRes = tmpVec;

        // determine the Levenberg-Marquardt parameter
        lmPar =
            determineLMParameter(
                qtf, delta, diag, internalData, solvedCols, work1, work2, work3, lmDir, lmPar);

        // compute the new point and the norm of the evolution direction
        double lmNorm = 0;
        for (int j = 0; j < solvedCols; ++j) {
          int pj = permutation[j];
          lmDir[pj] = -lmDir[pj];
          currentPoint[pj] = oldX[pj] + lmDir[pj];
          double s = diag[pj] * lmDir[pj];
          lmNorm += s * s;
        }
        lmNorm = FastMath.sqrt(lmNorm);
        // on the first iteration, adjust the initial step bound.
        if (firstIteration) {
          delta = FastMath.min(delta, lmNorm);
        }

        // Evaluate the function at x + p and calculate its norm.
        evaluationCounter.incrementCount();
        current = problem.evaluate(new ArrayRealVector(currentPoint));
        currentResiduals = current.getResiduals().toArray();
        currentCost = current.getCost();
        currentPoint = current.getPoint().toArray();

        // compute the scaled actual reduction
        double actRed = -1.0;
        if (0.1 * currentCost < previousCost) {
          double r = currentCost / previousCost;
          actRed = 1.0 - r * r;
        }

        // compute the scaled predicted reduction
        // and the scaled directional derivative
        for (int j = 0; j < solvedCols; ++j) {
          int pj = permutation[j];
          double dirJ = lmDir[pj];
          work1[j] = 0;
          for (int i = 0; i <= j; ++i) {
            work1[i] += weightedJacobian[i][pj] * dirJ;
          }
        }
        double coeff1 = 0;
        for (int j = 0; j < solvedCols; ++j) {
          coeff1 += work1[j] * work1[j];
        }
        double pc2 = previousCost * previousCost;
        coeff1 /= pc2;
        double coeff2 = lmPar * lmNorm * lmNorm / pc2;
        double preRed = coeff1 + 2 * coeff2;
        double dirDer = -(coeff1 + coeff2);

        // ratio of the actual to the predicted reduction
        ratio = (preRed == 0) ? 0 : (actRed / preRed);

        // update the step bound
        if (ratio <= 0.25) {
          double tmp = (actRed < 0) ? (0.5 * dirDer / (dirDer + 0.5 * actRed)) : 0.5;
          if ((0.1 * currentCost >= previousCost) || (tmp < 0.1)) {
            tmp = 0.1;
          }
          delta = tmp * FastMath.min(delta, 10.0 * lmNorm);
          lmPar /= tmp;
        } else if ((lmPar == 0) || (ratio >= 0.75)) {
          delta = 2 * lmNorm;
          lmPar *= 0.5;
        }

        // test for successful iteration.
        if (ratio >= 1.0e-4) {
          // successful iteration, update the norm
          firstIteration = false;
          xNorm = 0;
          for (int k = 0; k < nC; ++k) {
            double xK = diag[k] * currentPoint[k];
            xNorm += xK * xK;
          }
          xNorm = FastMath.sqrt(xNorm);

          // tests for convergence.
          if (checker != null
              && checker.converged(iterationCounter.getCount(), previous, current)) {
            return new OptimumImpl(
                current, evaluationCounter.getCount(), iterationCounter.getCount());
          }
        } else {
          // failed iteration, reset the previous values
          currentCost = previousCost;
          for (int j = 0; j < solvedCols; ++j) {
            int pj = permutation[j];
            currentPoint[pj] = oldX[pj];
          }
          tmpVec = weightedResidual;
          weightedResidual = oldRes;
          oldRes = tmpVec;
          // Reset "current" to previous values.
          current = previous;
        }

        // Default convergence criteria.
        if ((FastMath.abs(actRed) <= costRelativeTolerance
                && preRed <= costRelativeTolerance
                && ratio <= 2.0)
            || delta <= parRelativeTolerance * xNorm) {
          return new OptimumImpl(
              current, evaluationCounter.getCount(), iterationCounter.getCount());
        }

        // tests for termination and stringent tolerances
        if (FastMath.abs(actRed) <= TWO_EPS && preRed <= TWO_EPS && ratio <= 2.0) {
          throw new ConvergenceException(
              LocalizedFormats.TOO_SMALL_COST_RELATIVE_TOLERANCE, costRelativeTolerance);
        } else if (delta <= TWO_EPS * xNorm) {
          throw new ConvergenceException(
              LocalizedFormats.TOO_SMALL_PARAMETERS_RELATIVE_TOLERANCE, parRelativeTolerance);
        } else if (maxCosine <= TWO_EPS) {
          throw new ConvergenceException(
              LocalizedFormats.TOO_SMALL_ORTHOGONALITY_TOLERANCE, orthoTolerance);
        }
      }
    }
  }
  /** {@inheritDoc} */
  @Override
  protected PointValuePair doOptimize() {
    final ConvergenceChecker<PointValuePair> checker = getConvergenceChecker();
    final double[] point = getStartPoint();
    final GoalType goal = getGoalType();
    final int n = point.length;
    double[] r = computeObjectiveGradient(point);
    if (goal == GoalType.MINIMIZE) {
      for (int i = 0; i < n; i++) {
        r[i] = -r[i];
      }
    }

    // Initial search direction.
    double[] steepestDescent = preconditioner.precondition(point, r);
    double[] searchDirection = steepestDescent.clone();

    double delta = 0;
    for (int i = 0; i < n; ++i) {
      delta += r[i] * searchDirection[i];
    }

    PointValuePair current = null;
    while (true) {
      incrementIterationCount();

      final double objective = computeObjectiveValue(point);
      PointValuePair previous = current;
      current = new PointValuePair(point, objective);
      if (previous != null && checker.converged(getIterations(), previous, current)) {
        // We have found an optimum.
        return current;
      }

      final double step = line.search(point, searchDirection).getPoint();

      // Validate new point.
      for (int i = 0; i < point.length; ++i) {
        point[i] += step * searchDirection[i];
      }

      r = computeObjectiveGradient(point);
      if (goal == GoalType.MINIMIZE) {
        for (int i = 0; i < n; ++i) {
          r[i] = -r[i];
        }
      }

      // Compute beta.
      final double deltaOld = delta;
      final double[] newSteepestDescent = preconditioner.precondition(point, r);
      delta = 0;
      for (int i = 0; i < n; ++i) {
        delta += r[i] * newSteepestDescent[i];
      }

      final double beta;
      switch (updateFormula) {
        case FLETCHER_REEVES:
          beta = delta / deltaOld;
          break;
        case POLAK_RIBIERE:
          double deltaMid = 0;
          for (int i = 0; i < r.length; ++i) {
            deltaMid += r[i] * steepestDescent[i];
          }
          beta = (delta - deltaMid) / deltaOld;
          break;
        default:
          // Should never happen.
          throw new MathInternalError();
      }
      steepestDescent = newSteepestDescent;

      // Compute conjugate search direction.
      if (getIterations() % n == 0 || beta < 0) {
        // Break conjugation: reset search direction.
        searchDirection = steepestDescent.clone();
      } else {
        // Compute new conjugate search direction.
        for (int i = 0; i < n; ++i) {
          searchDirection[i] = steepestDescent[i] + beta * searchDirection[i];
        }
      }
    }
  }
예제 #4
0
  /** {@inheritDoc} */
  @Override
  protected UnivariatePointValuePair doOptimize() {
    final boolean isMinim = getGoalType() == GoalType.MINIMIZE;
    final double lo = getMin();
    final double mid = getStartValue();
    final double hi = getMax();

    // Optional additional convergence criteria.
    final ConvergenceChecker<UnivariatePointValuePair> checker = getConvergenceChecker();

    double a;
    double b;
    if (lo < hi) {
      a = lo;
      b = hi;
    } else {
      a = hi;
      b = lo;
    }

    double x = mid;
    double v = x;
    double w = x;
    double d = 0;
    double e = 0;
    double fx = computeObjectiveValue(x);
    if (!isMinim) {
      fx = -fx;
    }
    double fv = fx;
    double fw = fx;

    UnivariatePointValuePair previous = null;
    UnivariatePointValuePair current = new UnivariatePointValuePair(x, isMinim ? fx : -fx);
    // Best point encountered so far (which is the initial guess).
    UnivariatePointValuePair best = current;

    while (true) {
      final double m = 0.5 * (a + b);
      final double tol1 = relativeThreshold * FastMath.abs(x) + absoluteThreshold;
      final double tol2 = 2 * tol1;

      // Default stopping criterion.
      final boolean stop = FastMath.abs(x - m) <= tol2 - 0.5 * (b - a);
      if (!stop) {
        double p = 0;
        double q = 0;
        double r = 0;
        double u = 0;

        if (FastMath.abs(e) > tol1) { // Fit parabola.
          r = (x - w) * (fx - fv);
          q = (x - v) * (fx - fw);
          p = (x - v) * q - (x - w) * r;
          q = 2 * (q - r);

          if (q > 0) {
            p = -p;
          } else {
            q = -q;
          }

          r = e;
          e = d;

          if (p > q * (a - x) && p < q * (b - x) && FastMath.abs(p) < FastMath.abs(0.5 * q * r)) {
            // Parabolic interpolation step.
            d = p / q;
            u = x + d;

            // f must not be evaluated too close to a or b.
            if (u - a < tol2 || b - u < tol2) {
              if (x <= m) {
                d = tol1;
              } else {
                d = -tol1;
              }
            }
          } else {
            // Golden section step.
            if (x < m) {
              e = b - x;
            } else {
              e = a - x;
            }
            d = GOLDEN_SECTION * e;
          }
        } else {
          // Golden section step.
          if (x < m) {
            e = b - x;
          } else {
            e = a - x;
          }
          d = GOLDEN_SECTION * e;
        }

        // Update by at least "tol1".
        if (FastMath.abs(d) < tol1) {
          if (d >= 0) {
            u = x + tol1;
          } else {
            u = x - tol1;
          }
        } else {
          u = x + d;
        }

        double fu = computeObjectiveValue(u);
        if (!isMinim) {
          fu = -fu;
        }

        // User-defined convergence checker.
        previous = current;
        current = new UnivariatePointValuePair(u, isMinim ? fu : -fu);
        best = best(best, best(previous, current, isMinim), isMinim);

        if (checker != null && checker.converged(getIterations(), previous, current)) {
          return best;
        }

        // Update a, b, v, w and x.
        if (fu <= fx) {
          if (u < x) {
            b = x;
          } else {
            a = x;
          }
          v = w;
          fv = fw;
          w = x;
          fw = fx;
          x = u;
          fx = fu;
        } else {
          if (u < x) {
            a = u;
          } else {
            b = u;
          }
          if (fu <= fw || Precision.equals(w, x)) {
            v = w;
            fv = fw;
            w = u;
            fw = fu;
          } else if (fu <= fv || Precision.equals(v, x) || Precision.equals(v, w)) {
            v = u;
            fv = fu;
          }
        }
      } else { // Default termination (Brent's criterion).
        return best(best, best(previous, current, isMinim), isMinim);
      }

      incrementIterationCount();
    }
  }