MFEM  v4.6.0
Finite element discretization library
hiop.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #include "../config/config.hpp"
13 #include "hiop.hpp"
14 
15 #ifdef MFEM_USE_HIOP
16 #include <iostream>
17 
18 #include "hiopAlgFilterIPM.hpp"
19 
20 using namespace hiop;
21 
22 namespace mfem
23 {
24 
25 bool HiopOptimizationProblem::get_prob_sizes(size_type &n, size_type &m)
26 {
27  n = ntdofs_glob;
28  m = problem.GetNumConstraints();
29 
30  return true;
31 }
32 
33 bool HiopOptimizationProblem::get_starting_point(const size_type &n, double *x0)
34 {
35  MFEM_ASSERT(x_start != NULL && ntdofs_loc == x_start->Size(),
36  "Starting point is not set properly.");
37 
38  memcpy(x0, x_start->GetData(), ntdofs_loc * sizeof(double));
39 
40  return true;
41 }
42 
43 bool HiopOptimizationProblem::get_vars_info(const size_type &n,
44  double *xlow, double *xupp,
45  NonlinearityType *type)
46 {
47  MFEM_ASSERT(n == ntdofs_glob, "Global input mismatch.");
48  MFEM_ASSERT(problem.GetBoundsVec_Lo() && problem.GetBoundsVec_Hi(),
49  "Solution bounds are not set!");
50 
51  const int s = ntdofs_loc * sizeof(double);
52  std::memcpy(xlow, problem.GetBoundsVec_Lo()->GetData(), s);
53  std::memcpy(xupp, problem.GetBoundsVec_Hi()->GetData(), s);
54 
55  return true;
56 }
57 
58 bool HiopOptimizationProblem::get_cons_info(const size_type &m,
59  double *clow, double *cupp,
60  NonlinearityType *type)
61 {
62  MFEM_ASSERT(m == m_total, "Global constraint size mismatch.");
63 
64  int csize = 0;
65  if (problem.GetC())
66  {
67  csize = problem.GetEqualityVec()->Size();
68  const int s = csize * sizeof(double);
69  std::memcpy(clow, problem.GetEqualityVec()->GetData(), s);
70  std::memcpy(cupp, problem.GetEqualityVec()->GetData(), s);
71  }
72  if (problem.GetD())
73  {
74  const int s = problem.GetInequalityVec_Lo()->Size() * sizeof(double);
75  std::memcpy(clow + csize, problem.GetInequalityVec_Lo()->GetData(), s);
76  std::memcpy(cupp + csize, problem.GetInequalityVec_Hi()->GetData(), s);
77  }
78 
79  return true;
80 }
81 
82 bool HiopOptimizationProblem::eval_f(const size_type &n, const double *x,
83  bool new_x, double &obj_value)
84 {
85  MFEM_ASSERT(n == ntdofs_glob, "Global input mismatch.");
86 
87  if (new_x) { constr_info_is_current = false; }
88 
89  Vector x_vec(ntdofs_loc);
90  x_vec = x;
91  problem.new_x = new_x;
92  obj_value = problem.CalcObjective(x_vec);
93 
94  return true;
95 }
96 
97 bool HiopOptimizationProblem::eval_grad_f(const size_type &n, const double *x,
98  bool new_x, double *gradf)
99 {
100  MFEM_ASSERT(n == ntdofs_glob, "Global input mismatch.");
101 
102  if (new_x) { constr_info_is_current = false; }
103 
104  Vector x_vec(ntdofs_loc), gradf_vec(ntdofs_loc);
105  x_vec = x;
106  problem.new_x = new_x;
107  problem.CalcObjectiveGrad(x_vec, gradf_vec);
108  std::memcpy(gradf, gradf_vec.GetData(), ntdofs_loc * sizeof(double));
109 
110  return true;
111 }
112 
113 bool HiopOptimizationProblem::eval_cons(const size_type &n, const size_type &m,
114  const size_type &num_cons,
115  const index_type *idx_cons,
116  const double *x, bool new_x,
117  double *cons)
118 {
119  MFEM_ASSERT(n == ntdofs_glob, "Global input mismatch.");
120  MFEM_ASSERT(m == m_total, "Constraint size mismatch.");
121  MFEM_ASSERT(num_cons <= m, "num_cons should be at most m = " << m);
122 
123  if (num_cons == 0) { return true; }
124 
125  if (new_x) { constr_info_is_current = false; }
126  Vector x_vec(ntdofs_loc);
127  x_vec = x;
128  problem.new_x = new_x;
129  UpdateConstrValsGrads(x_vec);
130 
131  for (int c = 0; c < num_cons; c++)
132  {
133  MFEM_ASSERT(idx_cons[c] < m_total, "Constraint index is out of bounds.");
134  cons[c] = constr_vals(idx_cons[c]);
135  }
136 
137  return true;
138 }
139 
140 bool HiopOptimizationProblem::eval_Jac_cons(const size_type &n,
141  const size_type &m,
142  const size_type &num_cons,
143  const index_type *idx_cons,
144  const double *x, bool new_x,
145  double *Jac)
146 {
147  MFEM_ASSERT(n == ntdofs_glob, "Global input mismatch.");
148  MFEM_ASSERT(m == m_total, "Constraint size mismatch.");
149  MFEM_ASSERT(num_cons <= m, "num_cons should be at most m = " << m);
150 
151  if (num_cons == 0) { return true; }
152 
153  if (new_x) { constr_info_is_current = false; }
154  Vector x_vec(ntdofs_loc);
155  x_vec = x;
156  problem.new_x = new_x;
157  UpdateConstrValsGrads(x_vec);
158 
159  for (int c = 0; c < num_cons; c++)
160  {
161  MFEM_ASSERT(idx_cons[c] < m_total, "Constraint index is out of bounds.");
162  for (int j = 0; j < ntdofs_loc; j++)
163  {
164  // The matrix is stored by rows.
165  Jac[c * ntdofs_loc + j] = constr_grads(idx_cons[c], j);
166  }
167  }
168 
169  return true;
170 }
171 
172 bool HiopOptimizationProblem::get_vecdistrib_info(size_type global_n,
173  index_type *cols)
174 {
175 #ifdef MFEM_USE_MPI
176  int nranks;
177  MPI_Comm_size(comm, &nranks);
178 
179  size_type *sizes = new size_type[nranks];
180  MPI_Allgather(&ntdofs_loc, 1, MPI_HIOP_SIZE_TYPE, sizes, 1,
181  MPI_HIOP_SIZE_TYPE, comm);
182  cols[0] = 0;
183  for (int r = 1; r <= nranks; r++)
184  {
185  cols[r] = sizes[r-1] + cols[r-1];
186  }
187 
188  delete [] sizes;
189  return true;
190 #else
191  // Returning false means that Hiop runs in non-distributed mode.
192  return false;
193 #endif
194 }
195 
196 void HiopOptimizationProblem::solution_callback(hiop::hiopSolveStatus status,
197  hiop::size_type n,
198  const double *x,
199  const double *z_L,
200  const double *z_U,
201  hiop::size_type m,
202  const double *g,
203  const double *lambda,
204  double obj_value)
205 {
206  auto hp = dynamic_cast<const HiOpProblem *>(&problem);
207  if (!hp) { return; }
208  hp->SolutionCallback(status, n, x, z_L, z_U, m, g, lambda, obj_value);
209 }
210 
211 bool HiopOptimizationProblem::iterate_callback(int iter,
212  double obj_value,
213  double logbar_obj_value,
214  int n,
215  const double *x,
216  const double *z_L,
217  const double *z_U,
218  int m_ineq,
219  const double *s,
220  int m,
221  const double *g,
222  const double *lambda,
223  double inf_pr,
224  double inf_du,
225  double onenorm_pr_,
226  double mu,
227  double alpha_du,
228  double alpha_pr,
229  int ls_trials)
230 {
231  auto hp = dynamic_cast<const HiOpProblem *>(&problem);
232  if (!hp) { return true; }
233  return hp->IterateCallback(iter, obj_value, logbar_obj_value, n, x, z_L, z_U,
234  m_ineq, s, m, g, lambda, inf_pr, inf_du,
235  onenorm_pr_, mu, alpha_du, alpha_pr, ls_trials);
236 }
237 
238 void HiopOptimizationProblem::UpdateConstrValsGrads(const Vector x)
239 {
240  if (constr_info_is_current) { return; }
241 
242  // If needed (e.g. for CG spaces), communication should be handled by the
243  // operators' Mult() and GetGradient() methods.
244 
245  int cheight = 0;
246  if (problem.GetC())
247  {
248  cheight = problem.GetC()->Height();
249 
250  // Values of C.
251  Vector vals_C(constr_vals.GetData(), cheight);
252  problem.GetC()->Mult(x, vals_C);
253 
254  // Gradients C.
255  const Operator &oper_C = problem.GetC()->GetGradient(x);
256  const DenseMatrix *grad_C = dynamic_cast<const DenseMatrix *>(&oper_C);
257  MFEM_VERIFY(grad_C, "Hiop expects DenseMatrices as operator gradients.");
258  MFEM_ASSERT(grad_C->Height() == cheight && grad_C->Width() == ntdofs_loc,
259  "Incorrect dimensions of the C constraint gradient.");
260  for (int i = 0; i < cheight; i++)
261  {
262  for (int j = 0; j < ntdofs_loc; j++)
263  {
264  constr_grads(i, j) = (*grad_C)(i, j);
265  }
266  }
267  }
268 
269  if (problem.GetD())
270  {
271  const int dheight = problem.GetD()->Height();
272 
273  // Values of D.
274  Vector vals_D(constr_vals.GetData() + cheight, dheight);
275  problem.GetD()->Mult(x, vals_D);
276 
277  // Gradients of D.
278  const Operator &oper_D = problem.GetD()->GetGradient(x);
279  const DenseMatrix *grad_D = dynamic_cast<const DenseMatrix *>(&oper_D);
280  MFEM_VERIFY(grad_D, "Hiop expects DenseMatrices as operator gradients.");
281  MFEM_ASSERT(grad_D->Height() == dheight && grad_D->Width() == ntdofs_loc,
282  "Incorrect dimensions of the D constraint gradient.");
283  for (int i = 0; i < dheight; i++)
284  {
285  for (int j = 0; j < ntdofs_loc; j++)
286  {
287  constr_grads(i + cheight, j) = (*grad_D)(i, j);
288  }
289  }
290  }
291 
292  constr_info_is_current = true;
293 }
294 
295 HiopNlpOptimizer::HiopNlpOptimizer() : OptimizationSolver(), hiop_problem(NULL)
296 {
297 #ifdef MFEM_USE_MPI
298  // Set in case a serial driver uses a parallel MFEM build.
299  comm = MPI_COMM_WORLD;
300  int initialized, nret = MPI_Initialized(&initialized);
301  MFEM_ASSERT(MPI_SUCCESS == nret, "Failure in calling MPI_Initialized!");
302  if (!initialized)
303  {
304  nret = MPI_Init(NULL, NULL);
305  MFEM_ASSERT(MPI_SUCCESS == nret, "Failure in calling MPI_Init!");
306  }
307 #endif
308 }
309 
310 #ifdef MFEM_USE_MPI
312  : OptimizationSolver(comm_), hiop_problem(NULL), comm(comm_) { }
313 #endif
314 
316 {
317  delete hiop_problem;
318 }
319 
321 {
322  problem = &prob;
324 
325  if (hiop_problem) { delete hiop_problem; }
326 
327 #ifdef MFEM_USE_MPI
329 #else
331 #endif
332 }
333 
334 void HiopNlpOptimizer::Mult(const Vector &xt, Vector &x) const
335 {
336  MFEM_ASSERT(hiop_problem != NULL,
337  "Unspecified OptimizationProblem that must be solved.");
338 
340 
341  hiop::hiopNlpDenseConstraints hiopInstance(*hiop_problem);
342 
343  hiopInstance.options->SetNumericValue("rel_tolerance", rel_tol);
344  hiopInstance.options->SetNumericValue("tolerance", abs_tol);
345  hiopInstance.options->SetIntegerValue("max_iter", max_iter);
346 
347  hiopInstance.options->SetStringValue("fixed_var", "relax");
348  hiopInstance.options->SetNumericValue("fixed_var_tolerance", 1e-20);
349  hiopInstance.options->SetNumericValue("fixed_var_perturb", 1e-9);
350 
351  hiopInstance.options->SetNumericValue("mu0", 1e-1);
352 
353  // 0: no output; 3: not too much
354  hiopInstance.options->SetIntegerValue("verbosity_level", print_level);
355 
356  // Use the IPM solver.
357  hiop::hiopAlgFilterIPM solver(&hiopInstance);
358  const hiop::hiopSolveStatus status = solver.run();
359  final_norm = solver.getObjective();
360  final_iter = solver.getNumIterations();
361 
362  if (status != hiop::Solve_Success && status != hiop::Solve_Success_RelTol)
363  {
364  converged = false;
365  MFEM_WARNING("HIOP returned with a non-success status: " << status);
366  }
367  else { converged = true; }
368 
369  // Copy the final solution in x.
370  solver.getSolution(x.GetData());
371 }
372 
373 } // mfem namespace
374 #endif // MFEM_USE_HIOP
virtual void SetOptimizationProblem(const OptimizationProblem &prob)
Definition: hiop.cpp:320
void setStartingPoint(const Vector &x0)
Definition: hiop.hpp:85
Users can inherit this class to access to HiOp-specific functionality.
Definition: hiop.hpp:210
virtual void SolutionCallback(hiop::hiopSolveStatus status, hiop::size_type n, const double *x, const double *z_L, const double *z_U, hiop::size_type m, const double *g, const double *lambda, double obj_value) const
See hiopInterfaceBase::solution_callback(...).
Definition: hiop.hpp:217
virtual void Mult(const Vector &xt, Vector &x) const
Solves the optimization problem with xt as initial guess.
Definition: hiop.cpp:334
int print_level
(DEPRECATED) Legacy print level definition, which is left for compatibility with custom iterative sol...
Definition: solvers.hpp:130
virtual ~HiopNlpOptimizer()
Definition: hiop.cpp:315
prob_type prob
Definition: ex25.cpp:154
double * GetData() const
Return a pointer to the beginning of the Vector data.
Definition: vector.hpp:206
virtual bool IterateCallback(int iter, double obj_value, double logbar_obj_value, int n, const double *x, const double *z_L, const double *z_U, int m_ineq, const double *s, int m, const double *g, const double *lambda, double inf_pr, double inf_du, double onenorm_pr_, double mu, double alpha_du, double alpha_pr, int ls_trials) const
See hiopInterfaceBase::iterate_callback(...).
Definition: hiop.hpp:229
int problem
Definition: ex15.cpp:62
int max_iter
Limit for the number of iterations the solver is allowed to do.
Definition: solvers.hpp:151
int height
Dimension of the output / number of rows in the matrix.
Definition: operator.hpp:27
Abstract solver for OptimizationProblems.
Definition: solvers.hpp:865
double rel_tol
Relative tolerance.
Definition: solvers.hpp:154
double mu
Definition: ex25.cpp:140
Vector data type.
Definition: vector.hpp:58
RefCoord s[3]
const OptimizationProblem * problem
Definition: solvers.hpp:868
HiopOptimizationProblem * hiop_problem
Definition: hiop.hpp:255
int width
Dimension of the input / number of columns in the matrix.
Definition: operator.hpp:28
double abs_tol
Absolute tolerance.
Definition: solvers.hpp:157
Internal class - adapts the OptimizationProblem class to HiOp&#39;s interface.
Definition: hiop.hpp:32