Files
mfem/linalg/hiop.cpp
T
2020-03-09 15:36:41 -07:00

326 lines
9.9 KiB
C++

// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../config/config.hpp"
#include "hiop.hpp"
#ifdef MFEM_USE_HIOP
#include <iostream>
#include "hiopAlgFilterIPM.hpp"
using namespace hiop;
namespace mfem
{
bool HiopOptimizationProblem::get_prob_sizes(long long &n, long long &m)
{
n = ntdofs_glob;
m = problem.GetNumConstraints();
return true;
}
bool HiopOptimizationProblem::get_starting_point(const long long &n, double *x0)
{
MFEM_ASSERT(x_start != NULL && ntdofs_loc == x_start->Size(),
"Starting point is not set properly.");
memcpy(x0, x_start->GetData(), ntdofs_loc * sizeof(double));
return true;
}
bool HiopOptimizationProblem::get_vars_info(const long long &n,
double *xlow, double *xupp,
NonlinearityType *type)
{
MFEM_ASSERT(n == ntdofs_glob, "Global input mismatch.");
MFEM_ASSERT(problem.GetBoundsVec_Lo() && problem.GetBoundsVec_Hi(),
"Solution bounds are not set!");
const int s = ntdofs_loc * sizeof(double);
std::memcpy(xlow, problem.GetBoundsVec_Lo()->GetData(), s);
std::memcpy(xupp, problem.GetBoundsVec_Hi()->GetData(), s);
return true;
}
bool HiopOptimizationProblem::get_cons_info(const long long &m,
double *clow, double *cupp,
NonlinearityType *type)
{
MFEM_ASSERT(m == m_total, "Global constraint size mismatch.");
int csize = 0;
if (problem.GetC())
{
csize = problem.GetEqualityVec()->Size();
const int s = csize * sizeof(double);
std::memcpy(clow, problem.GetEqualityVec()->GetData(), s);
std::memcpy(cupp, problem.GetEqualityVec()->GetData(), s);
}
if (problem.GetD())
{
const int s = problem.GetInequalityVec_Lo()->Size() * sizeof(double);
std::memcpy(clow + csize, problem.GetInequalityVec_Lo()->GetData(), s);
std::memcpy(cupp + csize, problem.GetInequalityVec_Hi()->GetData(), s);
}
return true;
}
bool HiopOptimizationProblem::eval_f(const long long &n, const double *x,
bool new_x, double &obj_value)
{
MFEM_ASSERT(n == ntdofs_glob, "Global input mismatch.");
if (new_x) { constr_info_is_current = false; }
Vector x_vec(ntdofs_loc);
x_vec = x;
obj_value = problem.CalcObjective(x_vec);
return true;
}
bool HiopOptimizationProblem::eval_grad_f(const long long &n, const double *x,
bool new_x, double *gradf)
{
MFEM_ASSERT(n == ntdofs_glob, "Global input mismatch.");
if (new_x) { constr_info_is_current = false; }
Vector x_vec(ntdofs_loc), gradf_vec(ntdofs_loc);
x_vec = x;
problem.CalcObjectiveGrad(x_vec, gradf_vec);
std::memcpy(gradf, gradf_vec.GetData(), ntdofs_loc * sizeof(double));
return true;
}
bool HiopOptimizationProblem::eval_cons(const long long &n, const long long &m,
const long long &num_cons,
const long long *idx_cons,
const double *x, bool new_x,
double *cons)
{
MFEM_ASSERT(n == ntdofs_glob, "Global input mismatch.");
MFEM_ASSERT(m == m_total, "Constraint size mismatch.");
MFEM_ASSERT(num_cons <= m, "num_cons should be at most m = " << m);
if (num_cons == 0) { return true; }
if (new_x) { constr_info_is_current = false; }
Vector x_vec(ntdofs_loc);
x_vec = x;
UpdateConstrValsGrads(x_vec);
for (int c = 0; c < num_cons; c++)
{
MFEM_ASSERT(idx_cons[c] < m_total, "Constraint index is out of bounds.");
cons[c] = constr_vals(idx_cons[c]);
}
return true;
}
bool HiopOptimizationProblem::eval_Jac_cons(const long long &n,
const long long &m,
const long long &num_cons,
const long long *idx_cons,
const double *x, bool new_x,
double **Jac)
{
MFEM_ASSERT(n == ntdofs_glob, "Global input mismatch.");
MFEM_ASSERT(m == m_total, "Constraint size mismatch.");
MFEM_ASSERT(num_cons <= m, "num_cons should be at most m = " << m);
if (num_cons == 0) { return true; }
if (new_x) { constr_info_is_current = false; }
Vector x_vec(ntdofs_loc);
x_vec = x;
UpdateConstrValsGrads(x_vec);
for (int c = 0; c < num_cons; c++)
{
MFEM_ASSERT(idx_cons[c] < m_total, "Constraint index is out of bounds.");
for (int j = 0; j < ntdofs_loc; j++)
{
Jac[c][j] = constr_grads(idx_cons[c], j);
}
}
return true;
}
bool HiopOptimizationProblem::get_vecdistrib_info(long long global_n,
long long *cols)
{
#ifdef MFEM_USE_MPI
int nranks;
MPI_Comm_size(comm_, &nranks);
long long *sizes = new long long[nranks];
MPI_Allgather(&ntdofs_loc, 1, MPI_LONG_LONG_INT, sizes, 1,
MPI_LONG_LONG_INT, comm_);
cols[0] = 0;
for (int r = 1; r <= nranks; r++)
{
cols[r] = sizes[r-1] + cols[r-1];
}
delete [] sizes;
return true;
#else
// Returning false means that Hiop runs in non-distributed mode.
return false;
#endif
}
void HiopOptimizationProblem::UpdateConstrValsGrads(const Vector x)
{
if (constr_info_is_current) { return; }
// If needed (e.g. for CG spaces), communication should be handled by the
// operators' Mult() and GetGradient() methods.
int cheight = 0;
if (problem.GetC())
{
cheight = problem.GetC()->Height();
// Values of C.
Vector vals_C(constr_vals.GetData(), cheight);
problem.GetC()->Mult(x, vals_C);
// Gradients C.
const Operator &oper_C = problem.GetC()->GetGradient(x);
const DenseMatrix *grad_C = dynamic_cast<const DenseMatrix *>(&oper_C);
MFEM_VERIFY(grad_C, "Hiop expects DenseMatrices as operator gradients.");
MFEM_ASSERT(grad_C->Height() == cheight && grad_C->Width() == ntdofs_loc,
"Incorrect dimensions of the C constraint gradient.");
for (int i = 0; i < cheight; i++)
{
for (int j = 0; j < ntdofs_loc; j++)
{
constr_grads(i, j) = (*grad_C)(i, j);
}
}
}
if (problem.GetD())
{
const int dheight = problem.GetD()->Height();
// Values of D.
Vector vals_D(constr_vals.GetData() + cheight, dheight);
problem.GetD()->Mult(x, vals_D);
// Gradients of D.
const Operator &oper_D = problem.GetD()->GetGradient(x);
const DenseMatrix *grad_D = dynamic_cast<const DenseMatrix *>(&oper_D);
MFEM_VERIFY(grad_D, "Hiop expects DenseMatrices as operator gradients.");
MFEM_ASSERT(grad_D->Height() == dheight && grad_D->Width() == ntdofs_loc,
"Incorrect dimensions of the D constraint gradient.");
for (int i = 0; i < dheight; i++)
{
for (int j = 0; j < ntdofs_loc; j++)
{
constr_grads(i + cheight, j) = (*grad_D)(i, j);
}
}
}
constr_info_is_current = true;
}
HiopNlpOptimizer::HiopNlpOptimizer() : OptimizationSolver(), hiop_problem(NULL)
{
#ifdef MFEM_USE_MPI
// Set in case a serial driver uses a parallel MFEM build.
comm_ = MPI_COMM_WORLD;
int initialized, nret = MPI_Initialized(&initialized);
MFEM_ASSERT(MPI_SUCCESS == nret, "Failure in calling MPI_Initialized!");
if (!initialized)
{
nret = MPI_Init(NULL, NULL);
MFEM_ASSERT(MPI_SUCCESS == nret, "Failure in calling MPI_Init!");
}
#endif
}
#ifdef MFEM_USE_MPI
HiopNlpOptimizer::HiopNlpOptimizer(MPI_Comm _comm)
: OptimizationSolver(_comm), hiop_problem(NULL), comm_(_comm) { }
#endif
HiopNlpOptimizer::~HiopNlpOptimizer()
{
delete hiop_problem;
}
void HiopNlpOptimizer::SetOptimizationProblem(const OptimizationProblem &prob)
{
problem = &prob;
height = width = problem->input_size;
if (hiop_problem) { delete hiop_problem; }
#ifdef MFEM_USE_MPI
hiop_problem = new HiopOptimizationProblem(comm_, *problem);
#else
hiop_problem = new HiopOptimizationProblem(*problem);
#endif
}
void HiopNlpOptimizer::Mult(const Vector &xt, Vector &x) const
{
MFEM_ASSERT(hiop_problem != NULL,
"Unspecified OptimizationProblem that must be solved.");
hiop_problem->setStartingPoint(xt);
hiop::hiopNlpDenseConstraints hiopInstance(*hiop_problem);
hiopInstance.options->SetNumericValue("rel_tolerance", rel_tol);
hiopInstance.options->SetNumericValue("tolerance", abs_tol);
hiopInstance.options->SetIntegerValue("max_iter", max_iter);
hiopInstance.options->SetStringValue("fixed_var", "relax");
hiopInstance.options->SetNumericValue("fixed_var_tolerance", 1e-20);
hiopInstance.options->SetNumericValue("fixed_var_perturb", 1e-9);
// 0: no output; 3: not too much
hiopInstance.options->SetIntegerValue("verbosity_level", print_level);
// Use the IPM solver.
hiop::hiopAlgFilterIPM solver(&hiopInstance);
const hiop::hiopSolveStatus status = solver.run();
final_norm = solver.getObjective();
final_iter = solver.getNumIterations();
if (status != hiop::Solve_Success && status != hiop::Solve_Success_RelTol)
{
converged = false;
MFEM_WARNING("HIOP returned with a non-success status: " << status);
}
else { converged = true; }
// Copy the final solution in x.
solver.getSolution(x.GetData());
}
} // mfem namespace
#endif // MFEM_USE_HIOP