/// Given an indefinite matrix w.A, find a shift sigma
/// such that (A + sigma I) is positive definite.
/// @param sigma :: The result (shift).
/// @param d :: A solution vector to the system of linear equations
///    with the found positive defimnite matrix. The RHS vector is -w.v.
/// @param options :: The options.
/// @param inform :: The inform struct.
/// @param w :: The work struct.
void get_pd_shift(double &sigma, DoubleFortranVector &d,
                  const nlls_options &options, nlls_inform &inform,
                  more_sorensen_work &w) {
  int no_shifts = 0;
  bool successful_shift = false;
  while (!successful_shift) {
    shift_matrix(w.A, sigma, w.AplusSigma);
    solve_spd(w.AplusSigma, negative(w.v), w.LtL, d, inform);
    if (inform.status != NLLS_ERROR::OK) {
      // reset the error calls -- handled in the code....
      inform.status = NLLS_ERROR::OK;
      inform.external_return = 0;
      inform.external_name = "";
      no_shifts = no_shifts + 1;
      if (no_shifts == 10) { // too many shifts -- exit
        inform.status = NLLS_ERROR::MS_TOO_MANY_SHIFTS;
        return;
      }
      sigma = sigma + (pow(10.0, no_shifts)) * options.more_sorensen_shift;
    } else {
      successful_shift = true;
    }
  }
}
Beispiel #2
0
// least-squares fit a 3rd degree RPC model to the given data values in 3D
long double rpcfit33(
		long double p[20],    // output p coefficients
		long double q[20],    // output q coefficients (q[0] = 1)
		long double (*x)[3],  // input data positions
		long double *f,       // input data values
		int n                 // number of input points
		)
{
	// Input: n data points/values (x,f)
	// Output: a rational function p/q of degree 3
	// Algorithm: minimize E(p,q) = w(x)|p(x)-q(x)f|^2
	//            iteratively reweighted by w=1/q^2
	//
	// Note: the minimization problem is linear, symmetric
	// and positive definite.  The solution is not 0 since
	// the zero-degree coefficient of q is fixed at 1.
	// Thus the system has size 39x39.
	//
	// Note: this algorithm fails in two cases
	// 1) when the data points fit exactly to a rational function of lower
	// degree (e.g., a pinhole camera)
	// 2) when there are not enough data points
	// In these cases the matrix of the linear problem is degenerate and
	// the current solver fails, giving all-NAN, parameters.
	//

	// initialize q
	q[0] = 1;
	for (int i = 1; i < 20; i++)
		q[i] = 0;

	// build matrix "M" of evaluated monomials
	long double M[n][39];
	for (int i = 0; i < n; i++)
	{
		eval_mon20(M[i], x[i]);
		for (int j = 1; j < 20; j++)
			M[i][19+j] = -f[i] * M[i][j];
	}

	long double best_error_so_far = INFINITY;
	int best_iteration_so_far = -1;
	int number_of_iterations = 13;
	for (int iteration = 0; iteration < number_of_iterations; iteration++)
	{
		// vector "w" of weights
		long double w[n];
		for (int i = 0; i < n; i++)
			w[i] = pow( eval_pol20l(q, x[i]), -2);

		// matrix "A" of the system
		long double A[39][39] = {{0}};
		for (int i = 0; i < 39; i++)
		for (int j = 0; j < 39; j++)
		for (int k = 0; k < n ; k++)
			A[i][j] += w[k] * M[k][i] * M[k][j];

		// vector "b"
		long double b[39] = {0};
		for (int i = 0; i < 39; i++)
		for (int j = 0; j < n ; j++)
			b[i] += w[j] * f[j] * M[j][i];

		// solve the linear problem
		long double pq[39];
		solve_spd(pq, (void*)A, b, 39);

		// update if it improves error
		long double e = rpc33_errorpq(pq, x, f, n);
		fprintf(stderr, "\terru = %Lg\n", e);
		if (e < best_error_so_far)
		{
			best_error_so_far = e;
			best_iteration_so_far = iteration;
			for (int i = 0; i < 20; i++)
				p[i] = pq[i];
			for (int i = 1; i < 20; i++)
				q[i] = pq[19+i];
		}
	}

	fprintf(stderr, "best_iteration = %d\n", best_iteration_so_far);
	fprintf(stderr, "best_error = %Lg\n", best_error_so_far);
	return best_error_so_far;
}
/// Solve the trust-region subproblem using
/// the method of More and Sorensen
///
/// Using the implementation as in Algorithm 7.3.6
/// of Trust Region Methods
///
/// main output  d, the soln to the TR subproblem
/// @param J :: The Jacobian.
/// @param f :: The residuals.
/// @param hf :: The Hessian (sort of).
/// @param Delta :: The raduis of the trust region.
/// @param d :: The output vector of corrections to the parameters giving the
///       solution to the TR subproblem.
/// @param nd :: The 2-norm of d.
/// @param options :: The options.
/// @param inform :: The inform struct.
/// @param w :: The work struct.
void more_sorensen(const DoubleFortranMatrix &J, const DoubleFortranVector &f,
                   const DoubleFortranMatrix &hf, double Delta,
                   DoubleFortranVector &d, double &nd,
                   const nlls_options &options, nlls_inform &inform,
                   more_sorensen_work &w) {

  // The code finds
  //  d = arg min_p   v^T p + 0.5 * p^T A p
  //       s.t. ||p|| \leq Delta
  //
  // set A and v for the model being considered here...

  // Set A = J^T J
  matmult_inner(J, w.A);
  // add any second order information...
  // so A = J^T J + HF
  w.A += hf;
  // now form v = J^T f
  mult_Jt(J, f, w.v);

  // if scaling needed, do it
  if (options.scale != 0) {
    apply_scaling(J, w.A, w.v, w.apply_scaling_ws, options, inform);
  }

  auto n = J.len2();
  auto scale_back = [n, &d, &options, &w]() {
    if (options.scale != 0) {
      for (int i = 1; i <= n; ++i) {
        d(i) = d(i) / w.apply_scaling_ws.diag(i);
      }
    }
  };

  auto local_ms_shift = options.more_sorensen_shift;
  // d = -A\v
  DoubleFortranVector negv = w.v;
  negv *= -1.0;
  solve_spd(w.A, negv, w.LtL, d, inform);
  double sigma = 0.0;
  if (inform.status == NLLS_ERROR::OK) {
    // A is symmetric positive definite....
    sigma = zero;
  } else {
    // reset the error calls -- handled in the code....
    inform.status = NLLS_ERROR::OK;
    inform.external_return = 0;
    inform.external_name = "";
    min_eig_symm(w.A, sigma, w.y1);
    if (inform.status != NLLS_ERROR::OK) {
      scale_back();
      return;
    }
    sigma = -(sigma - local_ms_shift);
    // find a shift that makes (A + sigma I) positive definite
    get_pd_shift(sigma, d, options, inform, w);
    if (inform.status != NLLS_ERROR::OK) {
      scale_back();
      return;
    }
  }

  nd = norm2(d);
  if (boost::math::isnan(nd) || boost::math::isinf(nd)) {
    inform.status = NLLS_ERROR::NAN_OR_INF;
    throw std::runtime_error("Step is NaN or infinite.");
  }

  // now, we're not in the trust region initally, so iterate....
  auto sigma_shift = zero;
  int no_restarts = 0;
  // set 'small' in the context of the algorithm
  double epsilon =
      std::max(options.more_sorensen_tol * Delta, options.more_sorensen_tiny);
  int it = 1;
  for (; it <= options.more_sorensen_maxits; ++it) {

    if (nd <= Delta + epsilon) {
      // we're within the tr radius
      if (fabs(sigma) < options.more_sorensen_tiny ||
          fabs(nd - Delta) < epsilon) {
        // we're good....exit
        break;
      }
      if (w.y1.len() == n) {
        double alpha = 0.0;
        findbeta(d, w.y1, Delta, alpha, inform);
        if (inform.status == NLLS_ERROR::OK) {
          DoubleFortranVector tmp = w.y1;
          tmp *= alpha;
          d += tmp;
        }
      }
      // also good....exit
      break;
    }

    // w.q = R'\d
    // DTRSM( "Left", "Lower", "No Transpose", "Non-unit", n, 1, one, w.LtL, n,
    // w.q, n );
    for (int j = 1; j <= w.LtL.len1(); ++j) {
      for (int k = j + 1; k <= w.LtL.len1(); ++k) {
        w.LtL(j, k) = 0.0;
      }
    }
    w.LtL.solve(d, w.q);

    auto nq = norm2(w.q);
    sigma_shift = (pow((nd / nq), 2)) * ((nd - Delta) / Delta);
    if (fabs(sigma_shift) < options.more_sorensen_tiny * fabs(sigma)) {
      if (no_restarts < 1) {
        // find a shift that makes (A + sigma I) positive definite
        get_pd_shift(sigma, d, options, inform, w);
        if (inform.status != NLLS_ERROR::OK) {
          break;
        }
        no_restarts = no_restarts + 1;
      } else {
        // we're not going to make progress...jump out
        inform.status = NLLS_ERROR::MS_NO_PROGRESS;
        break;
      }
    } else {
      sigma = sigma + sigma_shift;
    }

    shift_matrix(w.A, sigma, w.AplusSigma);
    DoubleFortranVector negv = w.v;
    negv *= -1.0;
    solve_spd(w.AplusSigma, negv, w.LtL, d, inform);
    if (inform.status != NLLS_ERROR::OK) {
      break;
    }

    nd = norm2(d);
  }

  if (it == options.more_sorensen_maxits) {
    // maxits reached, not converged
    inform.status = NLLS_ERROR::MS_MAXITS;
  }
  scale_back();
}