DenseMatrix buildDenseMatrix(const std::string& expr1,
	                             const std::string& expr2,
	                             const MatrixReaderOptions& options)
	{
		auto m1 = readDenseMatrix(expr1, options);
		if(expr2 != "") {
			auto m2 = readDenseMatrix(expr2, options);
			m1.cbind(m2);
		}
		return m1;
	}
Beispiel #2
0
void runExperiment(options_t options) {

  // Declare local variables for frequently-used variables.
  int m = options.m;                                        // Number of rows
  int n = options.n;                                        // Number of columns
  int r = options.r;                                        // Rank
  int rt = options.r + int(options.USE_PCA);                // Rank of U (rt = r for non-PCA, rt = r + 1 for PCA)

  double sqrt_nu = options.sqrt_nu * int(!options.USE_PCA); // Regularization parameter (nu = nu for non-PCA, nu = 0 for PCA)

  // Initialize matrix pointers with adequate size.
  double* M = new double[m * n * DOUBLE_SIZE];
  double* W = new double[m * n * DOUBLE_SIZE];
  double* U = new double[m * rt * DOUBLE_SIZE];
  double* V = new double[n * r * DOUBLE_SIZE];

  // Set sample prefix.
  std::string samplePrefix = options.folder + "/" + options.dataset + "_r" + std::to_string(r);

  // Read matrix from pre-made binary files.
  if (!readDenseMatrix(samplePrefix + "_M.bin", M, m, n)) return;
  else if (!readDenseMatrix(samplePrefix + "_W.bin", W, m, n)) return;
  else if (!readDenseMatrix(samplePrefix + "_U0.bin", U, m, rt)) return;
  else if (!readDenseMatrix(samplePrefix + "_V0.bin", V, n, r)) return;

  // Declare a CERES problem.
  ceres::Problem problem;

  // Add row-wise parameter blocks of size rt from matrix U.
  for (int i = 0; i < m; i++) {
    problem.AddParameterBlock(&(U[i * rt]), rt);
  }
  // Add row-wise parameter blocks of size r from matrix V.
  for (int j = 0; j < n; j++) {
    problem.AddParameterBlock(&(V[j * r]), r);
  }

  if (options.USE_AUTO_DIFF) {
	// Use automatic derivatives

    // Declare a dynamic AD residual.
    ceres::DynamicAutoDiffCostFunction<AD_WeightedElementResidual, 4>* residual;

    // Add residuals of the original problem E(U,V).
    for (int i = 0; i < m; i++) {
      for (int j = 0; j < n; j++) {
        if (W[i*n + j] != 0) {
          residual = new ceres::DynamicAutoDiffCostFunction<AD_WeightedElementResidual, 4>(new AD_WeightedElementResidual(M[i*n + j], W[i*n + j], r, options.USE_PCA));
          residual->AddParameterBlock(rt);
          residual->AddParameterBlock(r);
          residual->SetNumResiduals(1);
          problem.AddResidualBlock(residual, NULL, &(U[i*rt]), &(V[j*r]));
        }
      }
    }

    // Add residuals of the regularization term vec(U) and vec(V).
    if (sqrt_nu > 0) {

      ceres::DynamicAutoDiffCostFunction<AD_RegularizationResidual, 4>* reg_residual;

      // Add residuals of vec(U)
      for (int i = 0; i < m; i++) {
        reg_residual = new ceres::DynamicAutoDiffCostFunction<AD_RegularizationResidual, 4>(new AD_RegularizationResidual(sqrt_nu, rt));
        reg_residual->AddParameterBlock(rt);
        reg_residual->SetNumResiduals(rt);
        problem.AddResidualBlock(reg_residual, NULL, &(U[i*rt]));
      }

      // Add residuals of vec(V)
      for (int j = 0; j < n; j++) {
        reg_residual = new ceres::DynamicAutoDiffCostFunction<AD_RegularizationResidual, 4>(new AD_RegularizationResidual(sqrt_nu, r));
        reg_residual->AddParameterBlock(r);
        reg_residual->SetNumResiduals(r);
        problem.AddResidualBlock(reg_residual, NULL, &(V[j*r]));
      }
    }

  } else {
	// Otherwise, use analytic derivatives.

    // Add residuals of the original problem E(U,V).
    for (int i = 0; i < m; i++)
      for (int j = 0; j < n; j++)
        if (W[i*n + j] != 0)
          problem.AddResidualBlock(new WeightedElementResidual(M[i*n + j], W[i*n + j], r, options.USE_PCA), NULL, &(U[i*rt]), &(V[j*r]));

    // Add residuals of the regularization term vec(U) and vec(V).
    if (sqrt_nu > 0) {

      // Add residuals of vec(U)
      for (int i = 0; i < m; i++) {
        problem.AddResidualBlock(new RegularizationResidual(sqrt_nu, r), NULL, &(U[i*rt]));
      }

      // Add residuals of vec(V)
      for (int j = 0; j < n; j++) {
        problem.AddResidualBlock(new RegularizationResidual(sqrt_nu, r), NULL, &(V[j*r]));
      }
    }
  }

  // Set CERES solver options.
  ceres::Solver::Options ce_opts;
  ce_opts.minimizer_progress_to_stdout = options.DISPLAY;
  ce_opts.max_num_iterations = options.max_iter;
  ce_opts.function_tolerance = options.tol;
  ce_opts.check_gradients = false;

  // Set the number of threads
  ce_opts.num_threads = options.nproc;
  ce_opts.num_linear_solver_threads = options.nproc;

  // Set the solver type and ordering.
  ce_opts.linear_solver_type = ceres::SPARSE_SCHUR;
  ce_opts.linear_solver_ordering.reset(new ceres::ParameterBlockOrdering);

  // Ordering depends on the input ELIMINATE_U_FIRST.
  for (int i = 0; i < m; ++i) {
    ce_opts.linear_solver_ordering->AddElementToGroup(&(U[i*rt]), int(!options.ELIMINATE_U_FIRST));
  }
  for (int j = 0; j < n; ++j) {
    ce_opts.linear_solver_ordering->AddElementToGroup(&(V[j*r]), int(options.ELIMINATE_U_FIRST));
  }

  // Set whether to use Jacobi scaling.
  ce_opts.jacobi_scaling = options.USE_JACOBI_SCALING;
  
  // Set options related to inner iterations.
  ce_opts.use_inner_iterations = options.USE_INNER_ITERS;

  if (options.USE_INNER_ITERS)
  {
    ce_opts.inner_iteration_tolerance = options.tol;

    // Set the inner iteration ordering.
    ce_opts.inner_iteration_ordering.reset(new ceres::ParameterBlockOrdering);

    // Ordering depends on the input ELIMINATE_U_FIRST.  
    for (int i = 0; i < m; ++i) {
      ce_opts.inner_iteration_ordering->AddElementToGroup(&(U[i*rt]), int(!options.ELIMINATE_U_FIRST));
    }
    for (int j = 0; j < n; ++j) {
      ce_opts.inner_iteration_ordering->AddElementToGroup(&(V[j*r]), int(options.ELIMINATE_U_FIRST));
    }
  }

  // Run the solver.
  ceres::Solver::Summary summary;
  Solve(ce_opts, &problem, &summary);

  // Output report.
  if (options.DISPLAY) {
    std::cout << summary.FullReport() << std::endl;
  }
  else {
    std::cout << summary.BriefReport() << std::endl;
  }
  std::cout << std::endl;

  // Write the output files including the number of iterations.
  double iters_[] = { double(summary.iterations.size() - 1) };
  writeDenseMatrix(samplePrefix + "_U.bin", U, m, rt);
  writeDenseMatrix(samplePrefix + "_V.bin", V, n, r);
  writeDenseMatrix(samplePrefix + "_iters.bin", iters_, 1, 1);

  // Free manually-allocated memory space.
  delete[] V;
  delete[] U;
  delete[] W;
  delete[] M;
}