MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
solvers.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, 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 "linalg.hpp"
13#include "lapack.hpp"
15#include "../general/forall.hpp"
18#include <iostream>
19#include <iomanip>
20#include <algorithm>
21#include <cmath>
22#include <set>
23
24namespace mfem
25{
26
27using namespace std;
28
30 : Solver(0, true)
31{
32 oper = NULL;
33 prec = NULL;
34 dot_oper = NULL;
35 max_iter = 10;
36 rel_tol = abs_tol = 0.0;
37#ifdef MFEM_USE_MPI
38 dot_prod_type = 0;
39#endif
40}
41
42#ifdef MFEM_USE_MPI
43
45 : Solver(0, true)
46{
47 oper = NULL;
48 prec = NULL;
49 dot_oper = NULL;
50 max_iter = 10;
51 rel_tol = abs_tol = 0.0;
52 dot_prod_type = 1;
53 comm = comm_;
54}
55
56#endif // MFEM_USE_MPI
57
58real_t IterativeSolver::Dot(const Vector &x, const Vector &y) const
59{
60 if (dot_oper) { return dot_oper->Eval(x,y); } // Use custom inner product (if provided)
61
62#ifndef MFEM_USE_MPI
63 return (x * y);
64#else
65 if (dot_prod_type == 0)
66 {
67 return (x * y);
68 }
69 else
70 {
71 return InnerProduct(comm, x, y);
72 }
73#endif
74}
75
77{
79 int print_level_ = print_lvl;
80
81#ifdef MFEM_USE_MPI
82 if (dot_prod_type != 0)
83 {
84 int rank;
85 MPI_Comm_rank(comm, &rank);
86 if (rank != 0) // Suppress output.
87 {
88 print_level_ = -1;
90 }
91 }
92#endif
93
94 print_level = print_level_;
95}
96
98{
99 print_options = options;
100
101 int derived_print_level = GuessLegacyPrintLevel(options);
102
103#ifdef MFEM_USE_MPI
104 if (dot_prod_type != 0)
105 {
106 int rank;
107 MPI_Comm_rank(comm, &rank);
108 if (rank != 0)
109 {
110 derived_print_level = -1;
112 }
113 }
114#endif
115
116 print_level = derived_print_level;
117}
118
120 int print_level_)
121{
122#ifdef MFEM_USE_MPI
123 int rank = 0;
124 if (comm != MPI_COMM_NULL)
125 {
126 MPI_Comm_rank(comm, &rank);
127 }
128#endif
129
130 switch (print_level_)
131 {
132 case -1:
133 return PrintLevel();
134 case 0:
135 return PrintLevel().Errors().Warnings();
136 case 1:
137 return PrintLevel().Errors().Warnings().Iterations();
138 case 2:
139 return PrintLevel().Errors().Warnings().Summary();
140 case 3:
141 return PrintLevel().Errors().Warnings().FirstAndLast();
142 default:
143#ifdef MFEM_USE_MPI
144 if (rank == 0)
145#endif
146 {
147 MFEM_WARNING("Unknown print level " << print_level_ <<
148 ". Defaulting to level 0.");
149 }
150 return PrintLevel().Errors().Warnings();
151 }
152}
153
155{
156 if (print_options_.iterations)
157 {
158 return 1;
159 }
160 else if (print_options_.first_and_last)
161 {
162 return 3;
163 }
164 else if (print_options_.summary)
165 {
166 return 2;
167 }
168 else if (print_options_.errors && print_options_.warnings)
169 {
170 return 0;
171 }
172 else
173 {
174 return -1;
175 }
176}
177
179{
180 prec = &pr;
181 prec->iterative_mode = false;
182}
183
185{
186 oper = &op;
187 height = op.Height();
188 width = op.Width();
189 if (prec)
190 {
192 }
193}
194
196 const Vector& x, bool final) const
197{
198 if (controller != nullptr)
199 {
200 if (it == 0 && !final)
201 {
202 controller->Reset();
203 }
204 controller->MonitorResidual(it, norm, r, final);
205 controller->MonitorSolution(it, norm, x, final);
206 return controller->HasConverged();
207 }
208 return false;
209}
210
212{
213 list.Read(); // TODO: just ensure 'list' is registered, no need to copy it
215 const int csz = constraint_list.Size();
216 xr.SetSize(csz); xr.UseDevice(true);
217 yr.SetSize(csz); yr.UseDevice(true);
218}
219
221{
222 const int csz = constraint_list.Size();
223
224 auto idx = constraint_list.Read();
225 auto d_x = x.Read();
226 auto d_y = y.Read();
227 auto d_xr = xr.ReadWrite();
228 auto d_yr = yr.ReadWrite();
229
230 mfem::forall(csz, [=] MFEM_HOST_DEVICE (int i)
231 {
232 d_xr[i] = d_x[idx[i]];
233 d_yr[i] = d_y[idx[i]];
234 });
235
236 return Dot(xr, yr);
237}
238
240{
241 const int csz = constraint_list.Size();
242 y.SetSize(csz);
243
244 auto idx = constraint_list.Read();
245 auto d_x = x.Read();
246 auto d_y = y.ReadWrite();
247
248 mfem::forall(csz, [=] MFEM_HOST_DEVICE (int i)
249 {
250 d_y[i] = d_x[idx[i]];
251 });
252}
253
255{
256 if (X && Y)
257 {
258 MFEM_VERIFY(X->Height() == Y->Height(),
259 "Incompatible operator heights in WeightedInnerProduct");
260 }
261
262 operX = X;
263 operY = Y;
264 if (operX)
265 {
268 const int ht = operX->Height();
269 wx.SetSize(ht, mem_type);
270 wx.UseDevice(true);
271 }
272
273 if (operY)
274 {
277 const int ht = operY->Height();
278 wy.SetSize(ht, mem_type);
279 wy.UseDevice(true);
280 }
281}
282
284{
285 MFEM_VERIFY((operX || operY),
286 "Weighting operator not set; set using ::SetOperator")
287
288 if (operY && operX)
289 {
290 operX->Mult(x, wx);
291 operY->Mult(y, wy);
292 return Dot(wy,wx);
293 }
294 else if (operY)
295 {
296 operY->Mult(y, wy);
297 return Dot(wy,x);
298 }
299 // if(operX)
300 operX->Mult(x, wx);
301 return Dot(y,wx);
302}
303
305{
306 MFEM_VERIFY((operX || operY),
307 "Weighting operator not set; set using ::SetOperator")
308
309 if (operY && operX)
310 {
311 operX->Mult(x, wx);
312 operY->Mult(wx, y);
313 return;
314 }
315 else if (operY)
316 {
317 operY->Mult(x, y);
318 return;
319 }
320 // if(operX)
321 operX->Mult(x, y);
322}
323
325 : damping(dmpng),
326 ess_tdof_list(nullptr),
327 oper(nullptr),
328 allow_updates(true)
329{ }
330
332 const Array<int> &ess_tdofs,
333 const real_t dmpng)
334 :
335 Solver(a.FESpace()->GetTrueVSize()),
336 dinv(height),
337 damping(dmpng),
338 ess_tdof_list(&ess_tdofs),
339 residual(height),
340 allow_updates(false)
341{
342 Vector &diag(residual);
343 a.AssembleDiagonal(diag);
344 // 'a' cannot be used for iterative_mode == true because its size may be
345 // different.
346 oper = nullptr;
347 Setup(diag);
348}
349
351 const Array<int> &ess_tdofs,
352 const real_t dmpng)
353 :
354 Solver(d.Size()),
355 dinv(height),
356 damping(dmpng),
357 ess_tdof_list(&ess_tdofs),
358 residual(height),
359 oper(NULL),
360 allow_updates(false)
361{
362 Setup(d);
363}
364
366{
367 if (!allow_updates)
368 {
369 // original behavior of this method
370 oper = &op; return;
371 }
372
373 // Treat (Par)BilinearForm objects as a special case since their
374 // AssembleDiagonal method returns the true-dof diagonal whereas the form
375 // itself may act as an ldof operator. This is for compatibility with the
376 // constructor that takes a BilinearForm parameter.
377 const BilinearForm *blf = dynamic_cast<const BilinearForm *>(&op);
378 if (blf)
379 {
380 // 'a' cannot be used for iterative_mode == true because its size may be
381 // different.
382 oper = nullptr;
383 height = width = blf->FESpace()->GetTrueVSize();
384 }
385 else
386 {
387 oper = &op;
388 height = op.Height();
389 width = op.Width();
390 MFEM_VERIFY(height == width, "not a square matrix!");
391 // ess_tdof_list is only used with BilinearForm
392 ess_tdof_list = nullptr;
393 }
394 dinv.SetSize(height);
395 residual.SetSize(height);
396 Vector &diag(residual);
397 op.AssembleDiagonal(diag);
398 Setup(diag);
399}
400
402{
403 residual.UseDevice(true);
404 const real_t delta = damping;
405 auto D = diag.Read();
406 auto DI = dinv.Write();
407 const bool use_abs_diag_ = use_abs_diag;
408 mfem::forall(height, [=] MFEM_HOST_DEVICE (int i)
409 {
410 if (D[i] == 0.0)
411 {
412 MFEM_ABORT_KERNEL("Zero diagonal entry in OperatorJacobiSmoother");
413 }
414 if (!use_abs_diag_) { DI[i] = delta / D[i]; }
415 else { DI[i] = delta / std::abs(D[i]); }
416 });
417 if (ess_tdof_list && ess_tdof_list->Size() > 0)
418 {
419 auto I = ess_tdof_list->Read();
420 mfem::forall(ess_tdof_list->Size(), [=] MFEM_HOST_DEVICE (int i)
421 {
422 DI[I[i]] = delta;
423 });
424 }
425}
426
428{
429 // For empty MPI ranks, height may be 0:
430 // MFEM_VERIFY(Height() > 0, "The diagonal hasn't been computed.");
431 MFEM_VERIFY(x.Size() == Width(), "invalid input vector");
432 MFEM_VERIFY(y.Size() == Height(), "invalid output vector");
433
434 if (iterative_mode)
435 {
436 MFEM_VERIFY(oper, "iterative_mode == true requires the forward operator");
437 oper->Mult(y, residual); // r = A y
438 subtract(x, residual, residual); // r = x - A y
439 }
440 else
441 {
442 residual = x;
443 y.UseDevice(true);
444 y = 0.0;
445 }
446 auto DI = dinv.Read();
447 auto R = residual.Read();
448 auto Y = y.ReadWrite();
449 mfem::forall(height, [=] MFEM_HOST_DEVICE (int i)
450 {
451 Y[i] += DI[i] * R[i];
452 });
453}
454
456 const Vector &d,
457 const Array<int>& ess_tdofs,
458 int order_, real_t max_eig_estimate_)
459 :
460 Solver(d.Size()),
461 order(order_),
462 max_eig_estimate(max_eig_estimate_),
463 N(d.Size()),
464 dinv(N),
465 diag(d),
466 coeffs(order),
467 ess_tdof_list(ess_tdofs),
468 residual(N),
469 oper(&oper_) { Setup(); }
470
471#ifdef MFEM_USE_MPI
473 const Vector &d,
474 const Array<int>& ess_tdofs,
475 int order_, MPI_Comm comm,
476 int power_iterations,
477 real_t power_tolerance,
478 int power_seed)
479#else
481 const Vector &d,
482 const Array<int>& ess_tdofs,
483 int order_,
484 int power_iterations,
485 real_t power_tolerance,
486 int power_seed)
487#endif
488 : Solver(d.Size()),
489 order(order_),
490 N(d.Size()),
491 dinv(N),
492 diag(d),
493 coeffs(order),
494 ess_tdof_list(ess_tdofs),
495 residual(N),
496 oper(&oper_)
497{
498 OperatorJacobiSmoother invDiagOperator(diag, ess_tdofs, 1.0);
499 ProductOperator diagPrecond(&invDiagOperator, oper, false, false);
500
501#ifdef MFEM_USE_MPI
502 PowerMethod powerMethod(comm);
503#else
504 PowerMethod powerMethod;
505#endif
506 Vector ev(oper->Width());
507 MFEM_VERIFY(power_seed != 0, "invalid seed!");
508 max_eig_estimate = powerMethod.EstimateLargestEigenvalue(diagPrecond, ev,
509 power_iterations,
510 power_tolerance,
511 power_seed);
512
513 Setup();
514}
515
517 const Vector &d,
518 const Array<int>& ess_tdofs,
519 int order_, real_t max_eig_estimate_)
520 : OperatorChebyshevSmoother(*oper_, d, ess_tdofs, order_, max_eig_estimate_) { }
521
522#ifdef MFEM_USE_MPI
524 const Vector &d,
525 const Array<int>& ess_tdofs,
526 int order_, MPI_Comm comm, int power_iterations, real_t power_tolerance)
527 : OperatorChebyshevSmoother(*oper_, d, ess_tdofs, order_, comm,
528 power_iterations, power_tolerance) { }
529#else
531 const Vector &d,
532 const Array<int>& ess_tdofs,
533 int order_, int power_iterations, real_t power_tolerance)
534 : OperatorChebyshevSmoother(*oper_, d, ess_tdofs, order_, power_iterations,
535 power_tolerance) { }
536#endif
537
539{
540 // Invert diagonal
541 residual.UseDevice(true);
542 helperVector.UseDevice(true);
543 auto D = diag.Read();
544 auto X = dinv.Write();
545 mfem::forall(N, [=] MFEM_HOST_DEVICE (int i) { X[i] = 1.0 / D[i]; });
546 auto I = ess_tdof_list.Read();
547 mfem::forall(ess_tdof_list.Size(), [=] MFEM_HOST_DEVICE (int i)
548 {
549 X[I[i]] = 1.0;
550 });
551
552 // Set up Chebyshev coefficients
553 // For reference, see e.g., Parallel multigrid smoothing: polynomial versus
554 // Gauss-Seidel by Adams et al.
555 real_t upper_bound = 1.2 * max_eig_estimate;
556 real_t lower_bound = 0.3 * max_eig_estimate;
557 real_t theta = 0.5 * (upper_bound + lower_bound);
558 real_t delta = 0.5 * (upper_bound - lower_bound);
559
560 switch (order-1)
561 {
562 case 0:
563 {
564 coeffs[0] = 1.0 / theta;
565 break;
566 }
567 case 1:
568 {
569 real_t tmp_0 = 1.0/(pow(delta, 2) - 2*pow(theta, 2));
570 coeffs[0] = -4*theta*tmp_0;
571 coeffs[1] = 2*tmp_0;
572 break;
573 }
574 case 2:
575 {
576 real_t tmp_0 = 3*pow(delta, 2);
577 real_t tmp_1 = pow(theta, 2);
578 real_t tmp_2 = 1.0/(-4*pow(theta, 3) + theta*tmp_0);
579 coeffs[0] = tmp_2*(tmp_0 - 12*tmp_1);
580 coeffs[1] = 12/(tmp_0 - 4*tmp_1);
581 coeffs[2] = -4*tmp_2;
582 break;
583 }
584 case 3:
585 {
586 real_t tmp_0 = pow(delta, 2);
587 real_t tmp_1 = pow(theta, 2);
588 real_t tmp_2 = 8*tmp_0;
589 real_t tmp_3 = 1.0/(pow(delta, 4) + 8*pow(theta, 4) - tmp_1*tmp_2);
590 coeffs[0] = tmp_3*(32*pow(theta, 3) - 16*theta*tmp_0);
591 coeffs[1] = tmp_3*(-48*tmp_1 + tmp_2);
592 coeffs[2] = 32*theta*tmp_3;
593 coeffs[3] = -8*tmp_3;
594 break;
595 }
596 case 4:
597 {
598 real_t tmp_0 = 5*pow(delta, 4);
599 real_t tmp_1 = pow(theta, 4);
600 real_t tmp_2 = pow(theta, 2);
601 real_t tmp_3 = pow(delta, 2);
602 real_t tmp_4 = 60*tmp_3;
603 real_t tmp_5 = 20*tmp_3;
604 real_t tmp_6 = 1.0/(16*pow(theta, 5) - pow(theta, 3)*tmp_5 + theta*tmp_0);
605 real_t tmp_7 = 160*tmp_2;
606 real_t tmp_8 = 1.0/(tmp_0 + 16*tmp_1 - tmp_2*tmp_5);
607 coeffs[0] = tmp_6*(tmp_0 + 80*tmp_1 - tmp_2*tmp_4);
608 coeffs[1] = tmp_8*(tmp_4 - tmp_7);
609 coeffs[2] = tmp_6*(-tmp_5 + tmp_7);
610 coeffs[3] = -80*tmp_8;
611 coeffs[4] = 16*tmp_6;
612 break;
613 }
614 default:
615 MFEM_ABORT("Chebyshev smoother not implemented for order = " << order);
616 }
617}
618
620{
621 if (iterative_mode)
622 {
623 MFEM_ABORT("Chebyshev smoother not implemented for iterative mode");
624 }
625
626 if (!oper)
627 {
628 MFEM_ABORT("Chebyshev smoother requires operator");
629 }
630
631 residual = x;
632 helperVector.SetSize(x.Size());
633 helperVector.UseDevice(true);
634
635 y.UseDevice(true);
636 y = 0.0;
637
638 for (int k = 0; k < order; ++k)
639 {
640 // Apply
641 if (k > 0)
642 {
643 oper->Mult(residual, helperVector);
644 residual = helperVector;
645 }
646
647 // Scale residual by inverse diagonal
648 const int n = N;
649 auto Dinv = dinv.Read();
650 auto R = residual.ReadWrite();
651 mfem::forall(n, [=] MFEM_HOST_DEVICE (int i) { R[i] *= Dinv[i]; });
652
653 // Add weighted contribution to y
654 auto Y = y.ReadWrite();
655 auto C = coeffs.Read();
656 mfem::forall(n, [=] MFEM_HOST_DEVICE (int i) { Y[i] += C[k] * R[i]; });
657 }
658}
659
661{
662 r.SetSize(width);
663 r.UseDevice(true);
664
665 z.SetSize(width);
666 z.UseDevice(true);
667}
668
669void SLISolver::Mult(const Vector &b, Vector &x) const
670{
671 const bool zero_b = (b.Size() == 0);
672 int i;
673
674 MFEM_VERIFY(!zero_b || iterative_mode || width == 0,
675 "empty 'b' can be used only in iterative mode!");
676
677 // Optimized preconditioned SLI with fixed number of iterations and given
678 // initial guess
679 if (rel_tol == 0.0 && iterative_mode && prec)
680 {
681 for (i = 0; i < max_iter; i++)
682 {
683 oper->Mult(x, r); // r = A x
684 if (!zero_b) { subtract(b, r, r); } // r = b - A x
685 prec->Mult(r, z); // z = B r
686 if (!zero_b) { x += z; } // x = x + B (b - A x)
687 else { x -= z; } // x = x - B (A x)
688 }
689 converged = true;
690 final_iter = i;
691 return;
692 }
693
694 // Optimized preconditioned SLI with fixed number of iterations and zero
695 // initial guess
696 if (rel_tol == 0.0 && !iterative_mode && prec)
697 {
698 prec->Mult(b, x); // x = B b (initial guess 0)
699 for (i = 1; i < max_iter; i++)
700 {
701 oper->Mult(x, r); // r = A x
702 subtract(b, r, r); // r = b - A x
703 prec->Mult(r, z); // z = B r
704 x += z; // x = x + B (b - A x)
705 }
706 converged = true;
707 final_iter = i;
708 return;
709 }
710
711 // General version of SLI with a relative tolerance, optional preconditioner
712 // and optional initial guess
713 real_t r0, nom, nom0, nomold = 1, cf;
714
715 if (iterative_mode)
716 {
717 oper->Mult(x, r);
718 if (!zero_b) { subtract(b, r, r); } // r = b - A x
719 }
720 else
721 {
722 r = b;
723 x = 0.0;
724 }
725
726 if (prec)
727 {
728 prec->Mult(r, z); // z = B r
729 nom0 = nom = sqrt(Dot(z, z));
730 }
731 else
732 {
733 nom0 = nom = sqrt(Dot(r, r));
734 }
735 initial_norm = nom0;
736
738 {
739 mfem::out << " Iteration : " << setw(3) << right << 0 << " ||Br|| = "
740 << nom << (print_options.first_and_last ? " ..." : "") << '\n';
741 }
742
743 r0 = std::max(nom*rel_tol, abs_tol);
744 if (Monitor(0, nom, r, x) || nom <= r0)
745 {
746 converged = true;
747 final_iter = 0;
748 final_norm = nom;
749 return;
750 }
751
752 // start iteration
753 converged = false;
755 for (i = 1; true; )
756 {
757 if (prec)
758 {
759 if (!zero_b) { x += z; } // x = x + B (b - A x)
760 else { x -= z; } // x = x - B (A x)
761 }
762 else
763 {
764 if (!zero_b) { x += r; } // x = x + (b - A x)
765 else { x -= r; } // x = x - (A x)
766 }
767
768 oper->Mult(x, r);
769 if (!zero_b) { subtract(b, r, r); } // r = b - A x
770
771 if (prec)
772 {
773 prec->Mult(r, z); // z = B r
774 nom = sqrt(Dot(z, z));
775 }
776 else
777 {
778 nom = sqrt(Dot(r, r));
779 }
780
781 cf = nom/nomold;
782 nomold = nom;
783
784 bool done = false;
785 if (Monitor(i, nom, r, x) || nom < r0)
786 {
787 converged = true;
788 final_iter = i;
789 done = true;
790 }
791
793 {
794 mfem::out << " Iteration : " << setw(3) << right << (i-1)
795 << " ||Br|| = " << setw(11) << left << nom
796 << "\tConv. rate: " << cf << '\n';
797 }
798
799 if (++i > max_iter)
800 {
801 done = true;
802 }
803
804 if (done) { break; }
805 }
806
808 {
809 const auto rf = pow (nom/nom0, 1.0/final_iter);
810 mfem::out << "SLI: Number of iterations: " << final_iter << '\n'
811 << "Conv. rate: " << cf << '\n'
812 << "Average reduction factor: "<< rf << '\n';
813 }
815 {
816 mfem::out << "SLI: No convergence!" << '\n';
817 }
818
819 final_norm = nom;
820 Monitor(final_iter, final_norm, r, x, true);
821}
822
823void SLI(const Operator &A, const Vector &b, Vector &x,
824 int print_iter, int max_num_iter,
825 real_t RTOLERANCE, real_t ATOLERANCE)
826{
827 MFEM_PERF_FUNCTION;
828
829 SLISolver sli;
830 sli.SetPrintLevel(print_iter);
831 sli.SetMaxIter(max_num_iter);
832 sli.SetRelTol(sqrt(RTOLERANCE));
833 sli.SetAbsTol(sqrt(ATOLERANCE));
834 sli.SetOperator(A);
835 sli.Mult(b, x);
836}
837
838void SLI(const Operator &A, Solver &B, const Vector &b, Vector &x,
839 int print_iter, int max_num_iter,
840 real_t RTOLERANCE, real_t ATOLERANCE)
841{
842 MFEM_PERF_FUNCTION;
843
844 SLISolver sli;
845 sli.SetPrintLevel(print_iter);
846 sli.SetMaxIter(max_num_iter);
847 sli.SetRelTol(sqrt(RTOLERANCE));
848 sli.SetAbsTol(sqrt(ATOLERANCE));
849 sli.SetOperator(A);
850 sli.SetPreconditioner(B);
851 sli.Mult(b, x);
852}
853
854
856{
858
859 r.SetSize(width, mt);
860 r.UseDevice(true);
861
862 d.SetSize(width, mt);
863 d.UseDevice(true);
864
865 z.SetSize(width, mt);
866 z.UseDevice(true);
867}
868
869void CGSolver::Mult(const Vector &b, Vector &x) const
870{
871 int i;
872 real_t r0, den, nom, nom0, betanom, alpha, beta;
873
874 x.UseDevice(true);
875 if (iterative_mode)
876 {
877 oper->Mult(x, r);
878 subtract(b, r, r); // r = b - A x
879 }
880 else
881 {
882 r = b;
883 x = 0.0;
884 }
885
886 if (prec)
887 {
888 prec->Mult(r, z); // z = B r
889 d = z;
890 }
891 else
892 {
893 d = r;
894 }
895 nom0 = nom = Dot(d, r);
896 if (nom0 >= 0.0) { initial_norm = sqrt(nom0); }
897 MFEM_VERIFY(IsFinite(nom), "nom = " << nom);
899 {
900 mfem::out << " Iteration : " << setw(3) << 0 << " (B r, r) = "
901 << nom << (print_options.first_and_last ? " ...\n" : "\n");
902 }
903
904 if (nom < 0.0)
905 {
907 {
908 mfem::out << "PCG: The preconditioner is not positive definite. (Br, r) = "
909 << nom << '\n';
910 }
911 converged = false;
912 final_iter = 0;
913 initial_norm = nom;
914 final_norm = nom;
915
916 Monitor(0, nom, r, x, true);
917 return;
918 }
919 r0 = std::max(nom*rel_tol*rel_tol, abs_tol*abs_tol);
920 if (Monitor(0, nom, r, x) || nom <= r0)
921 {
922 converged = true;
923 final_iter = 0;
924 final_norm = sqrt(nom);
925
926 Monitor(0, nom, r, x, true);
927 return;
928 }
929
930 oper->Mult(d, z); // z = A d
931 den = Dot(z, d);
932 MFEM_VERIFY(IsFinite(den), "den = " << den);
933 if (den <= 0.0)
934 {
935 if (Dot(d, d) > 0.0 && print_options.warnings)
936 {
937 mfem::out << "PCG: The operator is not positive definite. (Ad, d) = "
938 << den << '\n';
939 }
940 if (den == 0.0)
941 {
942 converged = false;
943 final_iter = 0;
944 final_norm = sqrt(nom);
945
946 Monitor(0, nom, r, x, true);
947 return;
948 }
949 }
950
951 // start iteration
952 converged = false;
954 for (i = 1; true; )
955 {
956 alpha = nom/den;
957 add(x, alpha, d, x); // x = x + alpha d
958 add(r, -alpha, z, r); // r = r - alpha A d
959
960 if (prec)
961 {
962 prec->Mult(r, z); // z = B r
963 betanom = Dot(r, z);
964 }
965 else
966 {
967 betanom = Dot(r, r);
968 }
969 MFEM_VERIFY(IsFinite(betanom), "betanom = " << betanom);
970 if (betanom < 0.0)
971 {
973 {
974 mfem::out << "PCG: The preconditioner is not positive definite. (Br, r) = "
975 << betanom << '\n';
976 }
977 converged = false;
978 final_iter = i;
979 break;
980 }
981
983 {
984 mfem::out << " Iteration : " << setw(3) << i << " (B r, r) = "
985 << betanom << std::endl;
986 }
987
988 if (Monitor(i, betanom, r, x) || betanom <= r0)
989 {
990 converged = true;
991 final_iter = i;
992 break;
993 }
994
995 if (++i > max_iter)
996 {
997 break;
998 }
999
1000 beta = betanom/nom;
1001 if (prec)
1002 {
1003 add(z, beta, d, d); // d = z + beta d
1004 }
1005 else
1006 {
1007 add(r, beta, d, d);
1008 }
1009 oper->Mult(d, z); // z = A d
1010 den = Dot(d, z);
1011 MFEM_VERIFY(IsFinite(den), "den = " << den);
1012 if (den <= 0.0)
1013 {
1014 if (Dot(d, d) > 0.0 && print_options.warnings)
1015 {
1016 mfem::out << "PCG: The operator is not positive definite. (Ad, d) = "
1017 << den << '\n';
1018 }
1019 if (den == 0.0)
1020 {
1021 final_iter = i;
1022 break;
1023 }
1024 }
1025 nom = betanom;
1026 }
1028 {
1029 mfem::out << " Iteration : " << setw(3) << final_iter << " (B r, r) = "
1030 << betanom << '\n';
1031 }
1033 {
1034 mfem::out << "PCG: Number of iterations: " << final_iter << '\n';
1035 }
1038 {
1039 const auto arf = pow (betanom/nom0, 0.5/final_iter);
1040 mfem::out << "Average reduction factor = " << arf << '\n';
1041 }
1043 {
1044 mfem::out << "PCG: No convergence!" << '\n';
1045 }
1046
1047 final_norm = sqrt(betanom);
1048
1049 Monitor(final_iter, final_norm, r, x, true);
1050}
1051
1052void CG(const Operator &A, const Vector &b, Vector &x,
1053 int print_iter, int max_num_iter,
1054 real_t RTOLERANCE, real_t ATOLERANCE)
1055{
1056 MFEM_PERF_FUNCTION;
1057
1058 CGSolver cg;
1059 cg.SetPrintLevel(print_iter);
1060 cg.SetMaxIter(max_num_iter);
1061 cg.SetRelTol(sqrt(RTOLERANCE));
1062 cg.SetAbsTol(sqrt(ATOLERANCE));
1063 cg.SetOperator(A);
1064 cg.Mult(b, x);
1065}
1066
1067void PCG(const Operator &A, Solver &B, const Vector &b, Vector &x,
1068 int print_iter, int max_num_iter,
1069 real_t RTOLERANCE, real_t ATOLERANCE)
1070{
1071 MFEM_PERF_FUNCTION;
1072
1073 CGSolver pcg;
1074 pcg.SetPrintLevel(print_iter);
1075 pcg.SetMaxIter(max_num_iter);
1076 pcg.SetRelTol(sqrt(RTOLERANCE));
1077 pcg.SetAbsTol(sqrt(ATOLERANCE));
1078 pcg.SetOperator(A);
1079 pcg.SetPreconditioner(B);
1080 pcg.Mult(b, x);
1081}
1082
1083
1085 real_t &cs, real_t &sn)
1086{
1087 if (dy == 0.0)
1088 {
1089 cs = 1.0;
1090 sn = 0.0;
1091 }
1092 else if (fabs(dy) > fabs(dx))
1093 {
1094 real_t temp = dx / dy;
1095 sn = 1.0 / sqrt( 1.0 + temp*temp );
1096 cs = temp * sn;
1097 }
1098 else
1099 {
1100 real_t temp = dy / dx;
1101 cs = 1.0 / sqrt( 1.0 + temp*temp );
1102 sn = temp * cs;
1103 }
1104}
1105
1106inline void ApplyPlaneRotation(real_t &dx, real_t &dy, real_t &cs, real_t &sn)
1107{
1108 real_t temp = cs * dx + sn * dy;
1109 dy = -sn * dx + cs * dy;
1110 dx = temp;
1111}
1112
1113inline void Update(Vector &x, int k, DenseMatrix &h, Vector &s,
1114 Array<Vector*> &v)
1115{
1116 Vector y(s);
1117
1118 // Backsolve:
1119 for (int i = k; i >= 0; i--)
1120 {
1121 y(i) /= h(i,i);
1122 for (int j = i - 1; j >= 0; j--)
1123 {
1124 y(j) -= h(j,i) * y(i);
1125 }
1126 }
1127
1128 for (int j = 0; j <= k; j++)
1129 {
1130 x.Add(y(j), *v[j]);
1131 }
1132}
1133
1134void GMRESSolver::Mult(const Vector &b, Vector &x) const
1135{
1136 // Generalized Minimum Residual method following the algorithm
1137 // on p. 20 of the SIAM Templates book.
1138
1139 int n = width;
1140
1141 DenseMatrix H(m+1, m);
1142 Vector s(m+1), cs(m+1), sn(m+1);
1143 Vector r(n), w(n), x_monitor;
1145
1146 b.UseDevice(true);
1147 x.UseDevice(true);
1148 r.UseDevice(true);
1149 w.UseDevice(true);
1150
1152 {
1153 x_monitor.SetSize(n);
1154 x_monitor.UseDevice(true);
1155 }
1156 else
1157 {
1158 x_monitor.MakeRef(x, 0, n);
1159 }
1160
1161 int i, j, k;
1162
1163 if (iterative_mode)
1164 {
1165 oper->Mult(x, r);
1166 }
1167 else
1168 {
1169 x = 0.0;
1170 }
1171
1172 if (prec)
1173 {
1174 if (iterative_mode)
1175 {
1176 subtract(b, r, w);
1177 prec->Mult(w, r); // r = M (b - A x)
1178 }
1179 else
1180 {
1181 prec->Mult(b, r);
1182 }
1183 }
1184 else
1185 {
1186 if (iterative_mode)
1187 {
1188 subtract(b, r, r);
1189 }
1190 else
1191 {
1192 r = b;
1193 }
1194 }
1195 real_t beta = initial_norm = Norm(r); // beta = ||r||
1196 MFEM_VERIFY(IsFinite(beta), "beta = " << beta);
1197
1198 final_norm = std::max(rel_tol*beta, abs_tol);
1199
1200 if (Monitor(0, beta, r, x) || beta <= final_norm)
1201 {
1202 final_norm = beta;
1203 final_iter = 0;
1204 converged = true;
1205 j = 0;
1206 goto finish;
1207 }
1208
1210 {
1211 mfem::out << " Pass : " << setw(2) << 1
1212 << " Iteration : " << setw(3) << 0
1213 << " ||B r|| = " << beta
1214 << (print_options.first_and_last ? " ...\n" : "\n");
1215 }
1216
1217 v.SetSize(m+1, NULL);
1218
1219 for (j = 1; j <= max_iter; )
1220 {
1221 if (v[0] == NULL)
1222 {
1223 v[0] = new Vector(n);
1224 v[0]->UseDevice(true);
1225 }
1226 v[0]->Set(1.0/beta, r);
1227 s = 0.0; s(0) = beta;
1228
1229 for (i = 0; i < m && j <= max_iter; i++, j++)
1230 {
1231 if (prec)
1232 {
1233 oper->Mult(*v[i], r);
1234 prec->Mult(r, w); // w = M A v[i]
1235 }
1236 else
1237 {
1238 oper->Mult(*v[i], w);
1239 }
1240
1241 for (k = 0; k <= i; k++)
1242 {
1243 H(k,i) = Dot(w, *v[k]); // H(k,i) = w * v[k]
1244 w.Add(-H(k,i), *v[k]); // w -= H(k,i) * v[k]
1245 }
1246
1247 H(i+1,i) = Norm(w); // H(i+1,i) = ||w||
1248 MFEM_VERIFY(IsFinite(H(i+1,i)), "Norm(w) = " << H(i+1,i));
1249 if (v[i+1] == NULL) { v[i+1] = new Vector(n); }
1250 v[i+1]->Set(1.0/H(i+1,i), w); // v[i+1] = w / H(i+1,i)
1251
1252 for (k = 0; k < i; k++)
1253 {
1254 ApplyPlaneRotation(H(k,i), H(k+1,i), cs(k), sn(k));
1255 }
1256
1257 GeneratePlaneRotation(H(i,i), H(i+1,i), cs(i), sn(i));
1258 ApplyPlaneRotation(H(i,i), H(i+1,i), cs(i), sn(i));
1259 ApplyPlaneRotation(s(i), s(i+1), cs(i), sn(i));
1260
1261 const real_t resid = fabs(s(i+1));
1262 MFEM_VERIFY(IsFinite(resid), "resid = " << resid);
1263
1265 {
1266 x_monitor = x;
1267 Update(x_monitor, i, H, s, v);
1268 }
1269
1270 if (Monitor(j, resid, r, x_monitor) || resid <= final_norm)
1271 {
1272 Update(x, i, H, s, v);
1273 final_norm = resid;
1274 final_iter = j;
1275 converged = true;
1276 goto finish;
1277 }
1278
1280 {
1281 mfem::out << " Pass : " << setw(2) << (j-1)/m+1
1282 << " Iteration : " << setw(3) << j
1283 << " ||B r|| = " << resid << '\n';
1284 }
1285 }
1286
1287 if (print_options.iterations && j <= max_iter)
1288 {
1289 mfem::out << "Restarting..." << '\n';
1290 }
1291
1292 Update(x, i-1, H, s, v);
1293
1294 oper->Mult(x, r);
1295 if (prec)
1296 {
1297 subtract(b, r, w);
1298 prec->Mult(w, r); // r = M (b - A x)
1299 }
1300 else
1301 {
1302 subtract(b, r, r);
1303 }
1304 beta = Norm(r); // beta = ||r||
1305 MFEM_VERIFY(IsFinite(beta), "beta = " << beta);
1306 if (beta <= final_norm)
1307 {
1308 final_norm = beta;
1309 final_iter = j;
1310 converged = true;
1311 goto finish;
1312 }
1313 }
1314
1315 final_norm = beta;
1317 converged = false;
1318
1319finish:
1321 {
1322 mfem::out << " Pass : " << setw(2) << (j-1)/m+1
1323 << " Iteration : " << setw(3) << final_iter
1324 << " ||B r|| = " << final_norm << '\n';
1325 }
1327 {
1328 mfem::out << "GMRES: Number of iterations: " << final_iter << '\n';
1329 }
1331 {
1332 mfem::out << "GMRES: No convergence!\n";
1333 }
1334
1335 Monitor(final_iter, final_norm, r, x, true);
1336
1337 for (i = 0; i < v.Size(); i++)
1338 {
1339 delete v[i];
1340 }
1341}
1342
1343void FGMRESSolver::Mult(const Vector &b, Vector &x) const
1344{
1345 DenseMatrix H(m+1,m);
1346 Vector s(m+1), cs(m+1), sn(m+1);
1347 Vector r(b.Size()), x_monitor;
1348
1349 b.UseDevice(true);
1350 x.UseDevice(true);
1351 r.UseDevice(true);
1352
1354 {
1355 x_monitor.SetSize(x.Size());
1356 x_monitor.UseDevice(true);
1357 }
1358 else
1359 {
1360 x_monitor.MakeRef(x, 0, x.Size());
1361 }
1362
1363 int i, j, k;
1364
1365 if (iterative_mode)
1366 {
1367 oper->Mult(x, r);
1368 subtract(b,r,r);
1369 }
1370 else
1371 {
1372 x = 0.;
1373 r = b;
1374 }
1375 real_t beta = initial_norm = Norm(r); // beta = ||r||
1376 MFEM_VERIFY(IsFinite(beta), "beta = " << beta);
1377
1378 final_norm = std::max(rel_tol*beta, abs_tol);
1379
1380 if (Monitor(0, beta, r, x) || beta <= final_norm)
1381 {
1382 converged = true;
1383 final_norm = beta;
1384 final_iter = 0;
1385
1386 Monitor(0, beta, r, x, true);
1387 return;
1388 }
1389
1390 // initialize the first pass
1391 converged = false;
1392
1394 {
1395 mfem::out << " Pass : " << setw(2) << 1
1396 << " Iteration : " << setw(3) << 0
1397 << " || r || = " << beta
1398 << (print_options.first_and_last ? " ...\n" : "\n");
1399 }
1400
1401 Array<Vector*> v(m+1);
1402 Array<Vector*> z(m+1);
1403 for (i= 0; i<=m; i++)
1404 {
1405 v[i] = NULL;
1406 z[i] = NULL;
1407 }
1408
1409 j = 1;
1410 while (j <= max_iter)
1411 {
1412 if (v[0] == NULL)
1413 {
1414 v[0] = new Vector(b.Size());
1415 v[0]->UseDevice(true);
1416 }
1417 (*v[0]) = 0.0;
1418 v[0] -> Add (1.0/beta, r); // v[0] = r / ||r||
1419 s = 0.0; s(0) = beta;
1420
1421 for (i = 0; i < m && j <= max_iter; i++, j++)
1422 {
1423
1424 if (z[i] == NULL)
1425 {
1426 z[i] = new Vector(b.Size());
1427 z[i]->UseDevice(true);
1428 }
1429 (*z[i]) = 0.0;
1430
1431 if (prec)
1432 {
1433 prec->Mult(*v[i], *z[i]);
1434 }
1435 else
1436 {
1437 (*z[i]) = (*v[i]);
1438 }
1439 oper->Mult(*z[i], r);
1440
1441 for (k = 0; k <= i; k++)
1442 {
1443 H(k,i) = Dot( r, *v[k]); // H(k,i) = r * v[k]
1444 r.Add(-H(k,i), (*v[k])); // r -= H(k,i) * v[k]
1445 }
1446
1447 H(i+1,i) = Norm(r); // H(i+1,i) = ||r||
1448 if (v[i+1] == NULL)
1449 {
1450 v[i+1] = new Vector(b.Size());
1451 v[i+1]->UseDevice(true);
1452 }
1453 (*v[i+1]) = 0.0;
1454 v[i+1] -> Add (1.0/H(i+1,i), r); // v[i+1] = r / H(i+1,i)
1455
1456 for (k = 0; k < i; k++)
1457 {
1458 ApplyPlaneRotation(H(k,i), H(k+1,i), cs(k), sn(k));
1459 }
1460
1461 GeneratePlaneRotation(H(i,i), H(i+1,i), cs(i), sn(i));
1462 ApplyPlaneRotation(H(i,i), H(i+1,i), cs(i), sn(i));
1463 ApplyPlaneRotation(s(i), s(i+1), cs(i), sn(i));
1464
1465 const real_t resid = fabs(s(i+1));
1466 MFEM_VERIFY(IsFinite(resid), "resid = " << resid);
1468 resid <= final_norm))
1469 {
1470 mfem::out << " Pass : " << setw(2) << (j-1)/m+1
1471 << " Iteration : " << setw(3) << j
1472 << " || r || = " << resid << endl;
1473 }
1474
1476 {
1477 x_monitor = x;
1478 Update(x_monitor, i, H, s, z);
1479 }
1480
1481 if (Monitor(j, resid, r, x_monitor) || resid <= final_norm)
1482 {
1483 Update(x, i, H, s, z);
1484 final_norm = resid;
1485 final_iter = j;
1486 converged = true;
1487
1489 {
1490 mfem::out << "FGMRES: Number of iterations: " << final_iter << '\n';
1491 }
1492
1493 for (i= 0; i<=m; i++)
1494 {
1495 if (v[i]) { delete v[i]; }
1496 if (z[i]) { delete z[i]; }
1497 }
1498
1499 Monitor(j, resid, r, x, true);
1500 return;
1501 }
1502 }
1503
1505 {
1506 mfem::out << "Restarting..." << endl;
1507 }
1508
1509 Update(x, i-1, H, s, z);
1510
1511 oper->Mult(x, r);
1512 subtract(b,r,r);
1513 beta = Norm(r);
1514 MFEM_VERIFY(IsFinite(beta), "beta = " << beta);
1515 if (beta <= final_norm)
1516 {
1517 converged = true;
1518
1519 break;
1520 }
1521 }
1522
1523 // Clean buffers up
1524 for (i = 0; i <= m; i++)
1525 {
1526 if (v[i]) { delete v[i]; }
1527 if (z[i]) { delete z[i]; }
1528 }
1529
1530 final_norm = beta;
1532
1533 // Note: j is off by one when we arrive here
1535 {
1536 mfem::out << " Pass : " << setw(2) << (j-1)/m+1
1537 << " Iteration : " << setw(3) << j-1
1538 << " || r || = " << final_norm << endl;
1539 }
1541 {
1542 mfem::out << "FGMRES: Number of iterations: " << final_iter << '\n';
1543 }
1545 {
1546 mfem::out << "FGMRES: No convergence!\n";
1547 }
1548
1549 Monitor(final_iter, final_norm, r, x, true);
1550}
1551
1552
1553int GMRES(const Operator &A, Vector &x, const Vector &b, Solver &M,
1554 int &max_iter, int m, real_t &tol, real_t atol, int printit)
1555{
1556 MFEM_PERF_FUNCTION;
1557
1558 GMRESSolver gmres;
1559 gmres.SetPrintLevel(printit);
1560 gmres.SetMaxIter(max_iter);
1561 gmres.SetKDim(m);
1562 gmres.SetRelTol(sqrt(tol));
1563 gmres.SetAbsTol(sqrt(atol));
1564 gmres.SetOperator(A);
1565 gmres.SetPreconditioner(M);
1566 gmres.Mult(b, x);
1567 max_iter = gmres.GetNumIterations();
1568 tol = gmres.GetFinalNorm()*gmres.GetFinalNorm();
1569 return gmres.GetConverged();
1570}
1571
1572void GMRES(const Operator &A, Solver &B, const Vector &b, Vector &x,
1573 int print_iter, int max_num_iter, int m, real_t rtol, real_t atol)
1574{
1575 GMRES(A, x, b, B, max_num_iter, m, rtol, atol, print_iter);
1576}
1577
1578
1580{
1581 p.SetSize(width);
1582 p.UseDevice(true);
1583
1585 phat.UseDevice(true);
1586
1587 s.SetSize(width);
1588 s.UseDevice(true);
1589
1591 shat.UseDevice(true);
1592
1593 t.SetSize(width);
1594 t.UseDevice(true);
1595
1596 v.SetSize(width);
1597 v.UseDevice(true);
1598
1599 r.SetSize(width);
1600 r.UseDevice(true);
1601
1603 rtilde.UseDevice(true);
1604}
1605
1606void BiCGSTABSolver::Mult(const Vector &b, Vector &x) const
1607{
1608 // BiConjugate Gradient Stabilized method following the algorithm
1609 // on p. 27 of the SIAM Templates book.
1610
1611 int i;
1612 real_t resid, tol_goal;
1613 real_t rho_1, rho_2=1.0, alpha=1.0, beta, omega=1.0;
1614
1615 b.UseDevice(true);
1616 x.UseDevice(true);
1617
1618 if (iterative_mode)
1619 {
1620 oper->Mult(x, r);
1621 subtract(b, r, r); // r = b - A x
1622 }
1623 else
1624 {
1625 x = 0.0;
1626 r = b;
1627 }
1628 rtilde = r;
1629
1630 resid = initial_norm = Norm(r);
1631 MFEM_VERIFY(IsFinite(resid), "resid = " << resid);
1633 {
1634 mfem::out << " Iteration : " << setw(3) << 0
1635 << " ||r|| = " << resid << (print_options.first_and_last ? " ...\n" : "\n");
1636 }
1637
1638 tol_goal = std::max(resid*rel_tol, abs_tol);
1639
1640 if (Monitor(0, resid, r, x) || resid <= tol_goal)
1641 {
1642 final_norm = resid;
1643 final_iter = 0;
1644 converged = true;
1645
1646 Monitor(0, resid, r, x, true);
1647 return;
1648 }
1649
1650 for (i = 1; i <= max_iter; i++)
1651 {
1652 rho_1 = Dot(rtilde, r);
1653 if (rho_1 == 0)
1654 {
1656 {
1657 mfem::out << " Iteration : " << setw(3) << i
1658 << " ||r|| = " << resid << '\n';
1659 }
1660
1661 final_norm = resid;
1662 final_iter = i;
1663 converged = false;
1665 {
1666 mfem::out << "BiCGStab: Number of iterations: " << final_iter << '\n';
1667 }
1669 {
1670 mfem::out << "BiCGStab: No convergence!\n";
1671 }
1672
1673 Monitor(i, resid, r, x, true);
1674 return;
1675 }
1676 if (i == 1)
1677 {
1678 p = r;
1679 }
1680 else
1681 {
1682 beta = (rho_1/rho_2) * (alpha/omega);
1683 add(p, -omega, v, p); // p = p - omega * v
1684 add(r, beta, p, p); // p = r + beta * p
1685 }
1686 if (prec)
1687 {
1688 prec->Mult(p, phat); // phat = M^{-1} * p
1689 }
1690 else
1691 {
1692 phat = p;
1693 }
1694 oper->Mult(phat, v); // v = A * phat
1695 alpha = rho_1 / Dot(rtilde, v);
1696 add(r, -alpha, v, s); // s = r - alpha * v
1697 resid = Norm(s);
1698 MFEM_VERIFY(IsFinite(resid), "resid = " << resid);
1699 if (Monitor(i, resid, r, x) || resid < tol_goal)
1700 {
1701 x.Add(alpha, phat); // x = x + alpha * phat
1703 {
1704 mfem::out << " Iteration : " << setw(3) << i
1705 << " ||s|| = " << resid << '\n';
1706 }
1707 final_norm = resid;
1708 final_iter = i;
1709 converged = true;
1711 {
1712 mfem::out << "BiCGStab: Number of iterations: " << final_iter << '\n';
1713 }
1714
1715 Monitor(i, resid, r, x, true);
1716 return;
1717 }
1719 {
1720 mfem::out << " Iteration : " << setw(3) << i
1721 << " ||s|| = " << resid;
1722 }
1723 if (prec)
1724 {
1725 prec->Mult(s, shat); // shat = M^{-1} * s
1726 }
1727 else
1728 {
1729 shat = s;
1730 }
1731 oper->Mult(shat, t); // t = A * shat
1732 omega = Dot(t, s) / Dot(t, t);
1733 x.Add(alpha, phat); // x += alpha * phat
1734 x.Add(omega, shat); // x += omega * shat
1735 add(s, -omega, t, r); // r = s - omega * t
1736
1737 rho_2 = rho_1;
1738 resid = Norm(r);
1739 MFEM_VERIFY(IsFinite(resid), "resid = " << resid);
1741 {
1742 mfem::out << " ||r|| = " << resid << '\n';
1743 }
1744 if (Monitor(i, resid, r, x) || resid < tol_goal)
1745 {
1746 final_norm = resid;
1747 final_iter = i;
1748 converged = true;
1750 {
1751 mfem::out << " Iteration : " << setw(3) << i
1752 << " ||r|| = " << resid << '\n';
1753 }
1755 {
1756 mfem::out << "BiCGStab: Number of iterations: " << final_iter << '\n';
1757 }
1758
1759 Monitor(i, resid, r, x, true);
1760 return;
1761 }
1762 if (omega == 0)
1763 {
1764 final_norm = resid;
1765 final_iter = i;
1766 converged = false;
1768 {
1769 mfem::out << " Iteration : " << setw(3) << i
1770 << " ||r|| = " << resid << '\n';
1771 }
1773 {
1774 mfem::out << "BiCGStab: Number of iterations: " << final_iter << '\n';
1775 }
1777 {
1778 mfem::out << "BiCGStab: No convergence!\n";
1779 }
1780
1781 Monitor(i, resid, r, x, true);
1782 return;
1783 }
1784 }
1785
1786 final_norm = resid;
1788 converged = false;
1789
1791 {
1792 mfem::out << " Iteration : " << setw(3) << final_iter
1793 << " ||r|| = " << resid << '\n';
1794 }
1796 {
1797 mfem::out << "BiCGStab: Number of iterations: " << final_iter << '\n';
1798 }
1800 {
1801 mfem::out << "BiCGStab: No convergence!\n";
1802 }
1803
1804 Monitor(final_iter, final_norm, r, x, true);
1805}
1806
1807int BiCGSTAB(const Operator &A, Vector &x, const Vector &b, Solver &M,
1808 int &max_iter, real_t &tol, real_t atol, int printit)
1809{
1810 BiCGSTABSolver bicgstab;
1811 bicgstab.SetPrintLevel(printit);
1812 bicgstab.SetMaxIter(max_iter);
1813 bicgstab.SetRelTol(sqrt(tol));
1814 bicgstab.SetAbsTol(sqrt(atol));
1815 bicgstab.SetOperator(A);
1816 bicgstab.SetPreconditioner(M);
1817 bicgstab.Mult(b, x);
1818 max_iter = bicgstab.GetNumIterations();
1819 tol = bicgstab.GetFinalNorm()*bicgstab.GetFinalNorm();
1820 return bicgstab.GetConverged();
1821}
1822
1823void BiCGSTAB(const Operator &A, Solver &B, const Vector &b, Vector &x,
1824 int print_iter, int max_num_iter, real_t rtol, real_t atol)
1825{
1826 BiCGSTAB(A, x, b, B, max_num_iter, rtol, atol, print_iter);
1827}
1828
1829
1831{
1833 v0.SetSize(width);
1834 v0.UseDevice(true);
1835
1836 v1.SetSize(width);
1837 v1.UseDevice(true);
1838
1839 w0.SetSize(width);
1840 w0.UseDevice(true);
1841
1842 w1.SetSize(width);
1843 w1.UseDevice(true);
1844
1845 q.SetSize(width);
1846 q.UseDevice(true);
1847
1848 if (prec)
1849 {
1850 u1.SetSize(width);
1851 u1.UseDevice(true);
1852 }
1853}
1854
1855void MINRESSolver::Mult(const Vector &b, Vector &x) const
1856{
1857 // Based on the MINRES algorithm on p. 86, Fig. 6.9 in
1858 // "Iterative Krylov Methods for Large Linear Systems",
1859 // by Henk A. van der Vorst, 2003.
1860 // Extended to support an SPD preconditioner.
1861
1862 b.UseDevice(true);
1863 x.UseDevice(true);
1864
1865 int it;
1866 real_t beta, eta, gamma0, gamma1, sigma0, sigma1;
1867 real_t alpha, delta, rho1, rho2, rho3, norm_goal;
1868 Vector *z = (prec) ? &u1 : &v1;
1869
1870 converged = true;
1871
1872 if (!iterative_mode)
1873 {
1874 v1 = b;
1875 x = 0.;
1876 }
1877 else
1878 {
1879 oper->Mult(x, v1);
1880 subtract(b, v1, v1);
1881 }
1882
1883 if (prec)
1884 {
1885 prec->Mult(v1, u1);
1886 }
1887 eta = beta = initial_norm = sqrt(Dot(*z, v1));
1888 MFEM_VERIFY(IsFinite(eta), "eta = " << eta);
1889 gamma0 = gamma1 = 1.;
1890 sigma0 = sigma1 = 0.;
1891
1892 norm_goal = std::max(rel_tol*eta, abs_tol);
1893
1894 if (Monitor(0, eta, *z, x) || eta <= norm_goal)
1895 {
1896 it = 0;
1897 goto loop_end;
1898 }
1899
1901 {
1902 mfem::out << "MINRES: iteration " << setw(3) << 0 << ": ||r||_B = "
1903 << eta << (print_options.first_and_last ? " ..." : "") << '\n';
1904 }
1905
1906 for (it = 1; it <= max_iter; it++)
1907 {
1908 v1 /= beta;
1909 if (prec)
1910 {
1911 u1 /= beta;
1912 }
1913 oper->Mult(*z, q);
1914 alpha = Dot(*z, q);
1915 MFEM_VERIFY(IsFinite(alpha), "alpha = " << alpha);
1916 if (it > 1) // (v0 == 0) for (it == 1)
1917 {
1918 q.Add(-beta, v0);
1919 }
1920 add(q, -alpha, v1, v0);
1921
1922 delta = gamma1*alpha - gamma0*sigma1*beta;
1923 rho3 = sigma0*beta;
1924 rho2 = sigma1*alpha + gamma0*gamma1*beta;
1925 if (!prec)
1926 {
1927 beta = Norm(v0);
1928 }
1929 else
1930 {
1931 prec->Mult(v0, q);
1932 beta = sqrt(Dot(v0, q));
1933 }
1934 MFEM_VERIFY(IsFinite(beta), "beta = " << beta);
1935 rho1 = std::hypot(delta, beta);
1936
1937 if (it == 1)
1938 {
1939 w0.Set(1./rho1, *z); // (w0 == 0) and (w1 == 0)
1940 }
1941 else if (it == 2)
1942 {
1943 add(1./rho1, *z, -rho2/rho1, w1, w0); // (w0 == 0)
1944 }
1945 else
1946 {
1947 add(-rho3/rho1, w0, -rho2/rho1, w1, w0);
1948 w0.Add(1./rho1, *z);
1949 }
1950
1951 gamma0 = gamma1;
1952 gamma1 = delta/rho1;
1953
1954 x.Add(gamma1*eta, w0);
1955
1956 sigma0 = sigma1;
1957 sigma1 = beta/rho1;
1958
1959 eta = -sigma1*eta;
1960 MFEM_VERIFY(IsFinite(eta), "eta = " << eta);
1961
1962 if (fabs(eta) <= norm_goal)
1963 {
1964 goto loop_end;
1965 }
1966
1968 {
1969 mfem::out << "MINRES: iteration " << setw(3) << it << ": ||r||_B = "
1970 << fabs(eta) << '\n';
1971 }
1972 if (Monitor(it, fabs(eta), *z, x)) { goto loop_end; }
1973
1974 if (prec)
1975 {
1976 Swap(u1, q);
1977 }
1978 Swap(v0, v1);
1979 Swap(w0, w1);
1980 }
1981 converged = false;
1982 it--;
1983
1984loop_end:
1985 final_iter = it;
1986 final_norm = fabs(eta);
1987
1989 {
1990 mfem::out << "MINRES: iteration " << setw(3) << it << ": ||r||_B = "
1991 << fabs(eta) << '\n';
1992 }
1993
1995 {
1996 mfem::out << "MINRES: Number of iterations: " << setw(3) << final_iter << '\n';
1997 }
1998
1999 converged = Monitor(final_iter, final_norm, *z, x, true) || converged;
2000
2001 // if (print_options.iteration_details || (!converged && print_options.errors))
2002 // {
2003 // oper->Mult(x, v1);
2004 // subtract(b, v1, v1);
2005 // if (prec)
2006 // {
2007 // prec->Mult(v1, u1);
2008 // }
2009 // eta = sqrt(Dot(*z, v1));
2010 // mfem::out << "MINRES: iteration " << setw(3) << it << '\n'
2011 // << " ||r||_B = " << eta << " (re-computed)" << '\n';
2012 // }
2013
2015 {
2016 mfem::out << "MINRES: No convergence!\n";
2017 }
2018}
2019
2020void MINRES(const Operator &A, const Vector &b, Vector &x, int print_it,
2021 int max_it, real_t rtol, real_t atol)
2022{
2023 MFEM_PERF_FUNCTION;
2024
2025 MINRESSolver minres;
2026 minres.SetPrintLevel(print_it);
2027 minres.SetMaxIter(max_it);
2028 minres.SetRelTol(sqrt(rtol));
2029 minres.SetAbsTol(sqrt(atol));
2030 minres.SetOperator(A);
2031 minres.Mult(b, x);
2032}
2033
2034void MINRES(const Operator &A, Solver &B, const Vector &b, Vector &x,
2035 int print_it, int max_it, real_t rtol, real_t atol)
2036{
2037 MINRESSolver minres;
2038 minres.SetPrintLevel(print_it);
2039 minres.SetMaxIter(max_it);
2040 minres.SetRelTol(sqrt(rtol));
2041 minres.SetAbsTol(sqrt(atol));
2042 minres.SetOperator(A);
2043 minres.SetPreconditioner(B);
2044 minres.Mult(b, x);
2045}
2046
2047
2049{
2050 oper = &op;
2051 height = op.Height();
2052 width = op.Width();
2053 MFEM_VERIFY(height == width, "square Operator is required.");
2054
2055 r.SetSize(width);
2056 r.UseDevice(true);
2057
2058 c.SetSize(width);
2059 c.UseDevice(true);
2060}
2061
2062void NewtonSolver::Mult(const Vector &b, Vector &x) const
2063{
2064 MFEM_VERIFY(oper != NULL, "the Operator is not set (use SetOperator).");
2065 MFEM_VERIFY(prec != NULL, "the Solver is not set (use SetSolver).");
2066
2067 int it;
2068 real_t norm0, norm, norm_goal;
2069 const bool have_b = (b.Size() == Height());
2070
2071 if (!iterative_mode)
2072 {
2073 x = 0.0;
2074 }
2075
2076 ProcessNewState(x);
2077
2078 oper->Mult(x, r);
2079 if (have_b)
2080 {
2081 r -= b;
2082 }
2083
2084 norm0 = norm = initial_norm = Norm(r);
2086 {
2087 mfem::out << "Newton iteration " << setw(2) << 0
2088 << " : ||r|| = " << norm << "...\n";
2089 }
2090 norm_goal = std::max(rel_tol*norm, abs_tol);
2091
2092 prec->iterative_mode = false;
2093
2094 // x_{i+1} = x_i - [DF(x_i)]^{-1} [F(x_i)-b]
2095 for (it = 0; true; it++)
2096 {
2097 MFEM_VERIFY(IsFinite(norm), "norm = " << norm);
2099 {
2100 mfem::out << "Newton iteration " << setw(2) << it
2101 << " : ||r|| = " << norm;
2102 if (it > 0)
2103 {
2104 mfem::out << ", ||r||/||r_0|| = " << norm/norm0;
2105 }
2106 mfem::out << '\n';
2107 }
2108
2109 if (Monitor(it, norm, r, x) || norm <= norm_goal)
2110 {
2111 converged = true;
2112 break;
2113 }
2114
2115 if (it >= max_iter)
2116 {
2117 converged = false;
2118 break;
2119 }
2120
2121 grad = &oper->GetGradient(x);
2123
2124 if (lin_rtol_type)
2125 {
2127 }
2128
2129 prec->Mult(r, c); // c = [DF(x_i)]^{-1} [F(x_i)-b]
2130
2131 if (lin_rtol_type)
2132 {
2134 }
2135
2136 const real_t c_scale = ComputeScalingFactor(x, b);
2137 if (c_scale == 0.0)
2138 {
2139 converged = false;
2140 break;
2141 }
2142 add(x, -c_scale, c, x);
2143
2144 ProcessNewState(x);
2145
2146 oper->Mult(x, r);
2147 if (have_b)
2148 {
2149 r -= b;
2150 }
2151 norm = Norm(r);
2152 }
2153
2154 final_iter = it;
2155 final_norm = norm;
2156
2159 {
2160 mfem::out << "Newton: Number of iterations: " << final_iter << '\n'
2161 << " ||r|| = " << final_norm
2162 << ", ||r||/||r_0|| = " << final_norm/norm0 << '\n';
2163 }
2165 {
2166 mfem::out << "Newton: No convergence!\n";
2167 }
2168
2169 Monitor(final_iter, final_norm, r, x, true);
2170}
2171
2173 const real_t rtol0,
2174 const real_t rtol_max,
2175 const real_t alpha_,
2176 const real_t gamma_)
2177{
2178 lin_rtol_type = type;
2179 lin_rtol0 = rtol0;
2180 lin_rtol_max = rtol_max;
2181 this->alpha = alpha_;
2182 this->gamma = gamma_;
2183}
2184
2186 const int it,
2187 const real_t fnorm) const
2188{
2189 // Assume that when adaptive linear solver relative tolerance is activated,
2190 // we are working with an iterative solver.
2191 auto iterative_solver = static_cast<IterativeSolver *>(prec);
2192 // Adaptive linear solver relative tolerance
2193 real_t eta;
2194 // Safeguard threshold
2195 real_t sg_threshold = 0.1;
2196
2197 if (it == 0)
2198 {
2199 eta = lin_rtol0;
2200 }
2201 else
2202 {
2203 if (lin_rtol_type == 1)
2204 {
2205 // eta = gamma * abs(||F(x1)|| - ||F(x0) + DF(x0) s0||) / ||F(x0)||
2206 eta = gamma * abs(fnorm - lnorm_last) / fnorm_last;
2207 }
2208 else if (lin_rtol_type == 2)
2209 {
2210 // eta = gamma * (||F(x1)|| / ||F(x0)||)^alpha
2211 eta = gamma * pow(fnorm / fnorm_last, alpha);
2212 }
2213 else
2214 {
2215 MFEM_ABORT("Unknown adaptive linear solver rtol version");
2216 }
2217
2218 // Safeguard rtol from "oversolving" ?!
2219 const real_t sg_eta = gamma * pow(eta_last, alpha);
2220 if (sg_eta > sg_threshold) { eta = std::max(eta, sg_eta); }
2221 }
2222
2223 eta = std::min(eta, lin_rtol_max);
2224 iterative_solver->SetRelTol(eta);
2225 eta_last = eta;
2227 {
2228 mfem::out << "Eisenstat-Walker rtol = " << eta << "\n";
2229 }
2230}
2231
2233 const Vector &b,
2234 const int it,
2235 const real_t fnorm) const
2236{
2237 fnorm_last = fnorm;
2238
2239 // If version 1 is chosen, the true linear residual norm has to be computed
2240 // and in most cases we can only retrieve the preconditioned linear residual
2241 // norm.
2242 if (lin_rtol_type == 1)
2243 {
2244 // lnorm_last = ||F(x0) + DF(x0) s0||
2245 Vector linres(x.Size());
2246 linres.UseDevice(true);
2247 grad->Mult(x, linres);
2248 linres -= b;
2249 lnorm_last = Norm(linres);
2250 }
2251}
2252
2253void LBFGSSolver::Mult(const Vector &b, Vector &x) const
2254{
2255 MFEM_VERIFY(oper != NULL, "the Operator is not set (use SetOperator).");
2256
2257 b.UseDevice(true);
2258 x.UseDevice(true);
2259
2260 // Quadrature points that are checked for negative Jacobians etc.
2261 Vector sk, rk, yk, rho, alpha;
2262
2263 // r - r_{k+1}, c - descent direction
2264 sk.SetSize(width); // x_{k+1}-x_k
2265 sk.UseDevice(true);
2266
2267 rk.SetSize(width); // nabla(f(x_{k}))
2268 rk.UseDevice(true);
2269
2270 yk.SetSize(width); // r_{k+1}-r_{k}
2271 yk.UseDevice(true);
2272
2273 rho.SetSize(m); // 1/(dot(yk,sk)
2274 rho.UseDevice(true);
2275
2276 alpha.SetSize(m); // rhok*sk'*c
2277 alpha.UseDevice(true);
2278
2279 int last_saved_id = -1;
2280
2281 int it;
2282 real_t norm0, norm, norm_goal;
2283 const bool have_b = (b.Size() == Height());
2284
2285 if (!iterative_mode)
2286 {
2287 x = 0.0;
2288 }
2289
2290 ProcessNewState(x);
2291
2292 // r = F(x)-b
2293 oper->Mult(x, r);
2294 if (have_b) { r -= b; }
2295
2296 c = r; // initial descent direction
2297
2298 norm0 = norm = initial_norm = Norm(r);
2300 {
2301 mfem::out << "LBFGS iteration " << setw(2) << 0
2302 << " : ||r|| = " << norm << "...\n";
2303 }
2304 norm_goal = std::max(rel_tol*norm, abs_tol);
2305 for (it = 0; true; it++)
2306 {
2307 MFEM_VERIFY(IsFinite(norm), "norm = " << norm);
2309 {
2310 mfem::out << "LBFGS iteration " << it
2311 << " : ||r|| = " << norm;
2312 if (it > 0)
2313 {
2314 mfem::out << ", ||r||/||r_0|| = " << norm/norm0;
2315 }
2316 mfem::out << '\n';
2317 }
2318
2319 if (norm <= norm_goal)
2320 {
2321 converged = true;
2322 break;
2323 }
2324
2325 if (it >= max_iter)
2326 {
2327 converged = false;
2328 break;
2329 }
2330
2331 rk = r;
2332 const real_t c_scale = ComputeScalingFactor(x, b);
2333 if (c_scale == 0.0)
2334 {
2335 converged = false;
2336 break;
2337 }
2338 add(x, -c_scale, c, x); // x_{k+1} = x_k - c_scale*c
2339
2340 ProcessNewState(x);
2341
2342 oper->Mult(x, r);
2343 if (have_b)
2344 {
2345 r -= b;
2346 }
2347
2348 // LBFGS - construct descent direction
2349 subtract(r, rk, yk); // yk = r_{k+1} - r_{k}
2350 sk = c; sk *= -c_scale; //sk = x_{k+1} - x_{k} = -c_scale*c
2351 const real_t gamma = Dot(sk, yk)/Dot(yk, yk);
2352
2353 // Save last m vectors
2354 last_saved_id = (last_saved_id == m-1) ? 0 : last_saved_id+1;
2355 *skArray[last_saved_id] = sk;
2356 *ykArray[last_saved_id] = yk;
2357
2358 c = r;
2359 for (int i = last_saved_id; i > -1; i--)
2360 {
2361 rho(i) = 1.0/Dot((*skArray[i]),(*ykArray[i]));
2362 alpha(i) = rho(i)*Dot((*skArray[i]),c);
2363 add(c, -alpha(i), (*ykArray[i]), c);
2364 }
2365 if (it > m-1)
2366 {
2367 for (int i = m-1; i > last_saved_id; i--)
2368 {
2369 rho(i) = 1./Dot((*skArray[i]), (*ykArray[i]));
2370 alpha(i) = rho(i)*Dot((*skArray[i]),c);
2371 add(c, -alpha(i), (*ykArray[i]), c);
2372 }
2373 }
2374
2375 c *= gamma; // scale search direction
2376 if (it > m-1)
2377 {
2378 for (int i = last_saved_id+1; i < m ; i++)
2379 {
2380 real_t betai = rho(i)*Dot((*ykArray[i]), c);
2381 add(c, alpha(i)-betai, (*skArray[i]), c);
2382 }
2383 }
2384 for (int i = 0; i < last_saved_id+1 ; i++)
2385 {
2386 real_t betai = rho(i)*Dot((*ykArray[i]), c);
2387 add(c, alpha(i)-betai, (*skArray[i]), c);
2388 }
2389
2390 norm = Norm(r);
2391 }
2392
2393 final_iter = it;
2394 final_norm = norm;
2395
2398 {
2399 mfem::out << "LBFGS: Number of iterations: " << final_iter << '\n'
2400 << " ||r|| = " << final_norm
2401 << ", ||r||/||r_0|| = " << final_norm/norm0 << '\n';
2402 }
2404 {
2405 mfem::out << "LBFGS: No convergence!\n";
2406 }
2407}
2408
2409int aGMRES(const Operator &A, Vector &x, const Vector &b,
2410 const Operator &M, int &max_iter,
2411 int m_max, int m_min, int m_step, real_t cf,
2412 real_t &tol, real_t &atol, int printit)
2413{
2414 int n = A.Width();
2415
2416 int m = m_max;
2417
2418 DenseMatrix H(m+1,m);
2419 Vector s(m+1), cs(m+1), sn(m+1);
2420 Vector w(n), av(n);
2421
2422 b.UseDevice(true);
2423 x.UseDevice(true);
2424 w.UseDevice(true);
2425 av.UseDevice(true);
2426
2427 real_t r1, resid;
2428 int i, j, k;
2429
2430 M.Mult(b,w);
2431 real_t normb = w.Norml2(); // normb = ||M b||
2432 if (normb == 0.0)
2433 {
2434 normb = 1;
2435 }
2436
2437 Vector r(n);
2438 r.UseDevice(true);
2439 A.Mult(x, r);
2440 subtract(b,r,w);
2441 M.Mult(w, r); // r = M (b - A x)
2442 real_t beta = r.Norml2(); // beta = ||r||
2443
2444 resid = beta / normb;
2445
2446 if (resid * resid <= tol)
2447 {
2448 tol = resid * resid;
2449 max_iter = 0;
2450 return 0;
2451 }
2452
2453 if (printit)
2454 {
2455 mfem::out << " Pass : " << setw(2) << 1
2456 << " Iteration : " << setw(3) << 0
2457 << " (r, r) = " << beta*beta << '\n';
2458 }
2459
2460 tol *= (normb*normb);
2461 tol = (atol > tol) ? atol : tol;
2462
2463 m = m_max;
2464 Array<Vector *> v(m+1);
2465 for (i= 0; i<=m; i++)
2466 {
2467 v[i] = new Vector(n);
2468 v[i]->UseDevice(true);
2469 (*v[i]) = 0.0;
2470 }
2471
2472 j = 1;
2473 while (j <= max_iter)
2474 {
2475 (*v[0]) = 0.0;
2476 v[0] -> Add (1.0/beta, r); // v[0] = r / ||r||
2477 s = 0.0; s(0) = beta;
2478
2479 r1 = beta;
2480
2481 for (i = 0; i < m && j <= max_iter; i++)
2482 {
2483 A.Mult((*v[i]),av);
2484 M.Mult(av,w); // w = M A v[i]
2485
2486 for (k = 0; k <= i; k++)
2487 {
2488 H(k,i) = w * (*v[k]); // H(k,i) = w * v[k]
2489 w.Add(-H(k,i), (*v[k])); // w -= H(k,i) * v[k]
2490 }
2491
2492 H(i+1,i) = w.Norml2(); // H(i+1,i) = ||w||
2493 (*v[i+1]) = 0.0;
2494 v[i+1] -> Add (1.0/H(i+1,i), w); // v[i+1] = w / H(i+1,i)
2495
2496 for (k = 0; k < i; k++)
2497 {
2498 ApplyPlaneRotation(H(k,i), H(k+1,i), cs(k), sn(k));
2499 }
2500
2501 GeneratePlaneRotation(H(i,i), H(i+1,i), cs(i), sn(i));
2502 ApplyPlaneRotation(H(i,i), H(i+1,i), cs(i), sn(i));
2503 ApplyPlaneRotation(s(i), s(i+1), cs(i), sn(i));
2504
2505 resid = fabs(s(i+1));
2506 if (printit)
2507 {
2508 mfem::out << " Pass : " << setw(2) << j
2509 << " Iteration : " << setw(3) << i+1
2510 << " (r, r) = " << resid*resid << '\n';
2511 }
2512
2513 if ( resid*resid < tol)
2514 {
2515 Update(x, i, H, s, v);
2516 tol = resid * resid;
2517 max_iter = j;
2518 for (i= 0; i<=m; i++)
2519 {
2520 delete v[i];
2521 }
2522 return 0;
2523 }
2524 }
2525
2526 if (printit)
2527 {
2528 mfem::out << "Restarting..." << '\n';
2529 }
2530
2531 Update(x, i-1, H, s, v);
2532
2533 A.Mult(x, r);
2534 subtract(b,r,w);
2535 M.Mult(w, r); // r = M (b - A x)
2536 beta = r.Norml2(); // beta = ||r||
2537 if ( resid*resid < tol)
2538 {
2539 tol = resid * resid;
2540 max_iter = j;
2541 for (i= 0; i<=m; i++)
2542 {
2543 delete v[i];
2544 }
2545 return 0;
2546 }
2547
2548 if (beta/r1 > cf)
2549 {
2550 if (m - m_step >= m_min)
2551 {
2552 m -= m_step;
2553 }
2554 else
2555 {
2556 m = m_max;
2557 }
2558 }
2559
2560 j++;
2561 }
2562
2563 tol = resid * resid;
2564 for (i= 0; i<=m; i++)
2565 {
2566 delete v[i];
2567 }
2568 return 1;
2569}
2570
2572 const Operator *C_,
2573 const Operator *D_)
2574 : C(C_), D(D_), c_e(NULL), d_lo(NULL), d_hi(NULL), x_lo(NULL), x_hi(NULL),
2575 input_size(insize)
2576{
2577 if (C) { MFEM_VERIFY(C->Width() == input_size, "Wrong width of C."); }
2578 if (D) { MFEM_VERIFY(D->Width() == input_size, "Wrong width of D."); }
2579}
2580
2582{
2583 MFEM_VERIFY(C, "The C operator is unspecified -- can't set constraints.");
2584 MFEM_VERIFY(c.Size() == C->Height(), "Wrong size of the constraint.");
2585
2586 c_e = &c;
2587}
2588
2590 const Vector &dh)
2591{
2592 MFEM_VERIFY(D, "The D operator is unspecified -- can't set constraints.");
2593 MFEM_VERIFY(dl.Size() == D->Height() && dh.Size() == D->Height(),
2594 "Wrong size of the constraint.");
2595
2596 d_lo = &dl; d_hi = &dh;
2597}
2598
2600{
2601 MFEM_VERIFY(xl.Size() == input_size && xh.Size() == input_size,
2602 "Wrong size of the constraint.");
2603
2604 x_lo = &xl; x_hi = &xh;
2605}
2606
2608{
2609 int m = 0;
2610 if (C) { m += C->Height(); }
2611 if (D) { m += D->Height(); }
2612 return m;
2613}
2614
2616{
2618 {
2619 MFEM_WARNING("Objective functional is ignored as SLBQP always minimizes"
2620 "the l2 norm of (x - x_target).");
2621 }
2622 MFEM_VERIFY(prob.GetC(), "Linear constraint is not set.");
2623 MFEM_VERIFY(prob.GetC()->Height() == 1, "Solver expects scalar constraint.");
2624
2625 problem = &prob;
2626}
2627
2628void SLBQPOptimizer::SetBounds(const Vector &lo_, const Vector &hi_)
2629{
2630 lo.SetDataAndSize(lo_.GetData(), lo_.Size());
2631 hi.SetDataAndSize(hi_.GetData(), hi_.Size());
2632}
2633
2635{
2636 w.SetDataAndSize(w_.GetData(), w_.Size());
2637 a = a_;
2638}
2639
2640inline void SLBQPOptimizer::print_iteration(int it, real_t r, real_t l) const
2641{
2643 {
2644 mfem::out << "SLBQP iteration " << it << ": residual = " << r
2645 << ", lambda = " << l << '\n';
2646 }
2647}
2648
2649void SLBQPOptimizer::Mult(const Vector& xt, Vector& x) const
2650{
2651 // Based on code provided by Denis Ridzal, dridzal@sandia.gov.
2652 // Algorithm adapted from Dai and Fletcher, "New Algorithms for
2653 // Singly Linearly Constrained Quadratic Programs Subject to Lower
2654 // and Upper Bounds", Numerical Analysis Report NA/216, 2003.
2655
2656 // Set some algorithm-specific constants and temporaries.
2657 int nclip = 0;
2658 real_t l = 0;
2659 real_t llow = 0;
2660 real_t lupp = 0;
2661 real_t lnew = 0;
2662 real_t dl = 2;
2663 real_t r = 0;
2664 real_t rlow = 0;
2665 real_t rupp = 0;
2666 real_t s = 0;
2667
2668 const real_t smin = 0.1;
2669
2670 const real_t tol = max(abs_tol, rel_tol*a);
2671
2672 // *** Start bracketing phase of SLBQP ***
2674 {
2675 mfem::out << "SLBQP bracketing phase" << '\n';
2676 }
2677
2678 // Solve QP with fixed Lagrange multiplier
2679 r = initial_norm = solve(l,xt,x,nclip);
2680 print_iteration(nclip, r, l);
2681
2682
2683 // If x=xt was already within bounds and satisfies the linear
2684 // constraint, then we already have the solution.
2685 if (fabs(r) <= tol)
2686 {
2687 converged = true;
2688 goto slbqp_done;
2689 }
2690
2691 if (r < 0)
2692 {
2693 llow = l; rlow = r; l = l + dl;
2694
2695 // Solve QP with fixed Lagrange multiplier
2696 r = solve(l,xt,x,nclip);
2697 print_iteration(nclip, r, l);
2698
2699 while ((r < 0) && (nclip < max_iter))
2700 {
2701 llow = l;
2702 s = rlow/r - 1.0;
2703 if (s < smin) { s = smin; }
2704 dl = dl + dl/s;
2705 l = l + dl;
2706
2707 // Solve QP with fixed Lagrange multiplier
2708 r = solve(l,xt,x,nclip);
2709 print_iteration(nclip, r, l);
2710 }
2711
2712 lupp = l; rupp = r;
2713 }
2714 else
2715 {
2716 lupp = l; rupp = r; l = l - dl;
2717
2718 // Solve QP with fixed Lagrange multiplier
2719 r = solve(l,xt,x,nclip);
2720 print_iteration(nclip, r, l);
2721
2722 while ((r > 0) && (nclip < max_iter))
2723 {
2724 lupp = l;
2725 s = rupp/r - 1.0;
2726 if (s < smin) { s = smin; }
2727 dl = dl + dl/s;
2728 l = l - dl;
2729
2730 // Solve QP with fixed Lagrange multiplier
2731 r = solve(l,xt,x,nclip);
2732 print_iteration(nclip, r, l);
2733 }
2734
2735 llow = l; rlow = r;
2736 }
2737
2738 // *** Stop bracketing phase of SLBQP ***
2739
2740
2741 // *** Start secant phase of SLBQP ***
2743 {
2744 mfem::out << "SLBQP secant phase" << '\n';
2745 }
2746
2747 s = 1.0 - rlow/rupp; dl = dl/s; l = lupp - dl;
2748
2749 // Solve QP with fixed Lagrange multiplier
2750 r = solve(l,xt,x,nclip);
2751 print_iteration(nclip, r, l);
2752
2753 while ( (fabs(r) > tol) && (nclip < max_iter) )
2754 {
2755 if (r > 0)
2756 {
2757 if (s <= 2.0)
2758 {
2759 lupp = l; rupp = r; s = 1.0 - rlow/rupp;
2760 dl = (lupp - llow)/s; l = lupp - dl;
2761 }
2762 else
2763 {
2764 s = rupp/r - 1.0;
2765 if (s < smin) { s = smin; }
2766 dl = (lupp - l)/s;
2767 lnew = 0.75*llow + 0.25*l;
2768 if (lnew < l-dl) { lnew = l-dl; }
2769 lupp = l; rupp = r; l = lnew;
2770 s = (lupp - llow)/(lupp - l);
2771 }
2772
2773 }
2774 else
2775 {
2776 if (s >= 2.0)
2777 {
2778 llow = l; rlow = r; s = 1.0 - rlow/rupp;
2779 dl = (lupp - llow)/s; l = lupp - dl;
2780 }
2781 else
2782 {
2783 s = rlow/r - 1.0;
2784 if (s < smin) { s = smin; }
2785 dl = (l - llow)/s;
2786 lnew = 0.75*lupp + 0.25*l;
2787 if (lnew < l+dl) { lnew = l+dl; }
2788 llow = l; rlow = r; l = lnew;
2789 s = (lupp - llow)/(lupp - l);
2790 }
2791 }
2792
2793 // Solve QP with fixed Lagrange multiplier
2794 r = solve(l,xt,x,nclip);
2795 print_iteration(nclip, r, l);
2796 }
2797
2798 // *** Stop secant phase of SLBQP ***
2799 converged = (fabs(r) <= tol);
2800
2801slbqp_done:
2802
2803 final_iter = nclip;
2804 final_norm = r;
2805
2808 {
2809 mfem::out << "SLBQP: Number of iterations: " << final_iter << '\n'
2810 << " lambda = " << l << '\n'
2811 << " ||r|| = " << final_norm << '\n';
2812 }
2814 {
2815 mfem::out << "SLBQP: No convergence!" << '\n';
2816 }
2817}
2818
2819struct WeightMinHeap
2820{
2821 const std::vector<real_t> &w;
2822 std::vector<size_t> c;
2823 std::vector<int> loc;
2824
2825 WeightMinHeap(const std::vector<real_t> &w_) : w(w_)
2826 {
2827 c.reserve(w.size());
2828 loc.resize(w.size());
2829 for (size_t i=0; i<w.size(); ++i) { push(i); }
2830 }
2831
2832 size_t percolate_up(size_t pos, real_t val)
2833 {
2834 for (; pos > 0 && w[c[(pos-1)/2]] > val; pos = (pos-1)/2)
2835 {
2836 c[pos] = c[(pos-1)/2];
2837 loc[c[(pos-1)/2]] = static_cast<int>(pos);
2838 }
2839 return pos;
2840 }
2841
2842 size_t percolate_down(size_t pos, real_t val)
2843 {
2844 while (2*pos+1 < c.size())
2845 {
2846 size_t left = 2*pos+1;
2847 size_t right = left+1;
2848 size_t tgt;
2849 if (right < c.size() && w[c[right]] < w[c[left]]) { tgt = right; }
2850 else { tgt = left; }
2851 if (w[c[tgt]] < val)
2852 {
2853 c[pos] = c[tgt];
2854 loc[c[tgt]] = static_cast<int>(pos);
2855 pos = tgt;
2856 }
2857 else
2858 {
2859 break;
2860 }
2861 }
2862 return pos;
2863 }
2864
2865 void push(size_t i)
2866 {
2867 real_t val = w[i];
2868 c.push_back(0);
2869 size_t pos = c.size()-1;
2870 pos = percolate_up(pos, val);
2871 c[pos] = i;
2872 loc[i] = static_cast<int>(pos);
2873 }
2874
2875 int pop()
2876 {
2877 size_t i = c[0];
2878 size_t j = c.back();
2879 c.pop_back();
2880 // Mark as removed
2881 loc[i] = -1;
2882 if (c.empty()) { return static_cast<int>(i); }
2883 real_t val = w[j];
2884 size_t pos = 0;
2885 pos = percolate_down(pos, val);
2886 c[pos] = j;
2887 loc[j] = static_cast<int>(pos);
2888 return static_cast<int>(i);
2889 }
2890
2891 void update(size_t i)
2892 {
2893 size_t pos = loc[i];
2894 real_t val = w[i];
2895 pos = percolate_up(pos, val);
2896 pos = percolate_down(pos, val);
2897 c[pos] = i;
2898 loc[i] = static_cast<int>(pos);
2899 }
2900
2901 bool picked(size_t i)
2902 {
2903 return loc[i] < 0;
2904 }
2905};
2906
2908{
2909 int n = C.Width();
2910 // Scale rows by reciprocal of diagonal and take absolute value
2911 Vector D;
2912 C.GetDiag(D);
2913 D.HostRead();
2914
2915 const int *I = C.HostReadI(), *J = C.HostReadJ();
2916 real_t *V = C.HostReadWriteData();
2917 for (int i=0; i<n; ++i)
2918 {
2919 for (int j=I[i]; j<I[i+1]; ++j)
2920 {
2921 V[j] = abs(V[j]/D[i]);
2922 }
2923 }
2924
2925 std::vector<real_t> w(n, 0.0);
2926 for (int k=0; k<n; ++k)
2927 {
2928 // Find all neighbors i of k
2929 for (int ii=I[k]; ii<I[k+1]; ++ii)
2930 {
2931 int i = J[ii];
2932 // Find value of (i,k)
2933 real_t C_ik = 0.0;
2934 for (int kk=I[i]; kk<I[i+1]; ++kk)
2935 {
2936 if (J[kk] == k)
2937 {
2938 C_ik = V[kk];
2939 break;
2940 }
2941 }
2942 for (int jj=I[k]; jj<I[k+1]; ++jj)
2943 {
2944 int j = J[jj];
2945 if (j == k) { continue; }
2946 real_t C_kj = V[jj];
2947 bool ij_exists = false;
2948 for (int jj2=I[i]; jj2<I[i+1]; ++jj2)
2949 {
2950 if (J[jj2] == j)
2951 {
2952 ij_exists = true;
2953 break;
2954 }
2955 }
2956 if (!ij_exists) { w[k] += pow(C_ik*C_kj,2); }
2957 }
2958 }
2959 w[k] = sqrt(w[k]);
2960 }
2961
2962 WeightMinHeap w_heap(w);
2963
2964 // Compute ordering
2965 p.SetSize(n);
2966 for (int ii=0; ii<n; ++ii)
2967 {
2968 int pi = w_heap.pop();
2969 p[ii] = pi;
2970 w[pi] = -1;
2971 for (int kk=I[pi]; kk<I[pi+1]; ++kk)
2972 {
2973 int k = J[kk];
2974 if (w_heap.picked(k)) { continue; }
2975 // Recompute weight
2976 w[k] = 0.0;
2977 // Find all neighbors i of k
2978 for (int ii2=I[k]; ii2<I[k+1]; ++ii2)
2979 {
2980 int i = J[ii2];
2981 if (w_heap.picked(i)) { continue; }
2982 // Find value of (i,k)
2983 real_t C_ik = 0.0;
2984 for (int kk2=I[i]; kk2<I[i+1]; ++kk2)
2985 {
2986 if (J[kk2] == k)
2987 {
2988 C_ik = V[kk2];
2989 break;
2990 }
2991 }
2992 for (int jj=I[k]; jj<I[k+1]; ++jj)
2993 {
2994 int j = J[jj];
2995 if (j == k || w_heap.picked(j)) { continue; }
2996 real_t C_kj = V[jj];
2997 bool ij_exists = false;
2998 for (int jj2=I[i]; jj2<I[i+1]; ++jj2)
2999 {
3000 if (J[jj2] == j)
3001 {
3002 ij_exists = true;
3003 break;
3004 }
3005 }
3006 if (!ij_exists) { w[k] += pow(C_ik*C_kj,2); }
3007 }
3008 }
3009 w[k] = sqrt(w[k]);
3010 w_heap.update(k);
3011 }
3012 }
3013}
3014
3015BlockILU::BlockILU(int block_size_,
3016 Reordering reordering_,
3017 int k_fill_)
3018 : Solver(0),
3019 block_size(block_size_),
3020 k_fill(k_fill_),
3021 reordering(reordering_)
3022{ }
3023
3025 int block_size_,
3026 Reordering reordering_,
3027 int k_fill_)
3028 : BlockILU(block_size_, reordering_, k_fill_)
3029{
3030 SetOperator(op);
3031}
3032
3034{
3035 const SparseMatrix *A = NULL;
3036#ifdef MFEM_USE_MPI
3037 const HypreParMatrix *A_par = dynamic_cast<const HypreParMatrix *>(&op);
3038 SparseMatrix A_par_diag;
3039 if (A_par != NULL)
3040 {
3041 A_par->GetDiag(A_par_diag);
3042 A = &A_par_diag;
3043 }
3044#endif
3045 if (A == NULL)
3046 {
3047 A = dynamic_cast<const SparseMatrix *>(&op);
3048 if (A == NULL)
3049 {
3050 MFEM_ABORT("BlockILU must be created with a SparseMatrix or HypreParMatrix");
3051 }
3052 }
3053 height = op.Height();
3054 width = op.Width();
3055 MFEM_VERIFY(A->Finalized(), "Matrix must be finalized.");
3056 CreateBlockPattern(*A);
3057 Factorize();
3058}
3059
3060void BlockILU::CreateBlockPattern(const SparseMatrix &A)
3061{
3062 MFEM_VERIFY(k_fill == 0, "Only block ILU(0) is currently supported.");
3063 if (A.Height() % block_size != 0)
3064 {
3065 MFEM_ABORT("BlockILU: block size must evenly divide the matrix size");
3066 }
3067
3068 int nrows = A.Height();
3069 const int *I = A.HostReadI();
3070 const int *J = A.HostReadJ();
3071 const real_t *V = A.HostReadData();
3072 int nnz = 0;
3073 int nblockrows = nrows / block_size;
3074
3075 std::vector<std::set<int>> unique_block_cols(nblockrows);
3076
3077 for (int iblock = 0; iblock < nblockrows; ++iblock)
3078 {
3079 for (int bi = 0; bi < block_size; ++bi)
3080 {
3081 int i = iblock * block_size + bi;
3082 for (int k = I[i]; k < I[i + 1]; ++k)
3083 {
3084 unique_block_cols[iblock].insert(J[k] / block_size);
3085 }
3086 }
3087 nnz += static_cast<int>(unique_block_cols[iblock].size());
3088 }
3089
3090 if (reordering != Reordering::NONE)
3091 {
3092 SparseMatrix C(nblockrows, nblockrows);
3093 for (int iblock = 0; iblock < nblockrows; ++iblock)
3094 {
3095 for (int jblock : unique_block_cols[iblock])
3096 {
3097 for (int bi = 0; bi < block_size; ++bi)
3098 {
3099 int i = iblock * block_size + bi;
3100 for (int k = I[i]; k < I[i + 1]; ++k)
3101 {
3102 int j = J[k];
3103 if (j >= jblock * block_size && j < (jblock + 1) * block_size)
3104 {
3105 C.Add(iblock, jblock, V[k]*V[k]);
3106 }
3107 }
3108 }
3109 }
3110 }
3111 C.Finalize(false);
3112 real_t *CV = C.GetData();
3113 for (int i=0; i<C.NumNonZeroElems(); ++i)
3114 {
3115 CV[i] = sqrt(CV[i]);
3116 }
3117
3118 switch (reordering)
3119 {
3122 break;
3123 default:
3124 MFEM_ABORT("BlockILU: unknown reordering")
3125 }
3126 }
3127 else
3128 {
3129 // No reordering: permutation is identity
3130 P.SetSize(nblockrows);
3131 for (int i=0; i<nblockrows; ++i)
3132 {
3133 P[i] = i;
3134 }
3135 }
3136
3137 // Compute inverse permutation
3138 Pinv.SetSize(nblockrows);
3139 for (int i=0; i<nblockrows; ++i)
3140 {
3141 Pinv[P[i]] = i;
3142 }
3143
3144 // Permute columns
3145 std::vector<std::vector<int>> unique_block_cols_perminv(nblockrows);
3146 for (int i=0; i<nblockrows; ++i)
3147 {
3148 std::vector<int> &cols = unique_block_cols_perminv[i];
3149 for (int j : unique_block_cols[P[i]])
3150 {
3151 cols.push_back(Pinv[j]);
3152 }
3153 std::sort(cols.begin(), cols.end());
3154 }
3155
3156 ID.SetSize(nblockrows);
3157 IB.SetSize(nblockrows + 1);
3158 IB[0] = 0;
3159 JB.SetSize(nnz);
3160 AB.SetSize(block_size, block_size, nnz);
3161 DB.SetSize(block_size, block_size, nblockrows);
3162 AB = 0.0;
3163 DB = 0.0;
3164 ipiv.SetSize(block_size*nblockrows);
3165 int counter = 0;
3166
3167 for (int iblock = 0; iblock < nblockrows; ++iblock)
3168 {
3169 int iblock_perm = P[iblock];
3170 for (int jblock : unique_block_cols_perminv[iblock])
3171 {
3172 int jblock_perm = P[jblock];
3173 if (iblock == jblock)
3174 {
3175 ID[iblock] = counter;
3176 }
3177 JB[counter] = jblock;
3178 for (int bi = 0; bi < block_size; ++bi)
3179 {
3180 int i = iblock_perm*block_size + bi;
3181 for (int k = I[i]; k < I[i + 1]; ++k)
3182 {
3183 int j = J[k];
3184 if (j >= jblock_perm*block_size && j < (jblock_perm + 1)*block_size)
3185 {
3186 int bj = j - jblock_perm*block_size;
3187 real_t val = V[k];
3188 AB(bi, bj, counter) = val;
3189 // Extract the diagonal
3190 if (iblock == jblock)
3191 {
3192 DB(bi, bj, iblock) = val;
3193 }
3194 }
3195 }
3196 }
3197 ++counter;
3198 }
3199 IB[iblock + 1] = counter;
3200 }
3201}
3202
3203void BlockILU::Factorize()
3204{
3205 int nblockrows = Height()/block_size;
3206
3207 // Precompute LU factorization of diagonal blocks
3208 for (int i=0; i<nblockrows; ++i)
3209 {
3210 LUFactors factorization(DB.GetData(i), &ipiv[i*block_size]);
3211 factorization.Factor(block_size);
3212 }
3213
3214 // Note: we use UseExternalData to extract submatrices from the tensor AB
3215 // instead of the DenseTensor call operator, because the call operator does
3216 // not allow for two simultaneous submatrix views into the same tensor
3217 DenseMatrix A_ik, A_ij, A_kj;
3218 // Loop over block rows (starting with second block row)
3219 for (int i=1; i<nblockrows; ++i)
3220 {
3221 // Find all nonzeros to the left of the diagonal in row i
3222 for (int kk=IB[i]; kk<IB[i+1]; ++kk)
3223 {
3224 int k = JB[kk];
3225 // Make sure we're still to the left of the diagonal
3226 if (k == i) { break; }
3227 if (k > i)
3228 {
3229 MFEM_ABORT("Matrix must be sorted with nonzero diagonal");
3230 }
3231 LUFactors A_kk_inv(DB.GetData(k), &ipiv[k*block_size]);
3232 A_ik.UseExternalData(&AB(0,0,kk), block_size, block_size);
3233 // A_ik = A_ik * A_kk^{-1}
3234 A_kk_inv.RightSolve(block_size, block_size, A_ik.GetData());
3235 // Modify everything to the right of k in row i
3236 for (int jj=kk+1; jj<IB[i+1]; ++jj)
3237 {
3238 int j = JB[jj];
3239 if (j <= k) { continue; } // Superfluous because JB is sorted?
3240 A_ij.UseExternalData(&AB(0,0,jj), block_size, block_size);
3241 for (int ll=IB[k]; ll<IB[k+1]; ++ll)
3242 {
3243 int l = JB[ll];
3244 if (l == j)
3245 {
3246 A_kj.UseExternalData(&AB(0,0,ll), block_size, block_size);
3247 // A_ij = A_ij - A_ik*A_kj;
3248 AddMult_a(-1.0, A_ik, A_kj, A_ij);
3249 // If we need to, update diagonal factorization
3250 if (j == i)
3251 {
3252 DB(i) = A_ij;
3253 LUFactors factorization(DB.GetData(i), &ipiv[i*block_size]);
3254 factorization.Factor(block_size);
3255 }
3256 break;
3257 }
3258 }
3259 }
3260 }
3261 }
3262}
3263
3264void BlockILU::Mult(const Vector &b, Vector &x) const
3265{
3266 MFEM_VERIFY(height > 0, "BlockILU(0) preconditioner is not constructed");
3267 int nblockrows = Height()/block_size;
3268 y.SetSize(Height());
3269
3270 DenseMatrix B;
3271 Vector yi, yj, xi, xj;
3272 Vector tmp(block_size);
3273 // Forward substitute to solve Ly = b
3274 // Implicitly, L has identity on the diagonal
3275 y = 0.0;
3276 for (int i=0; i<nblockrows; ++i)
3277 {
3278 yi.SetDataAndSize(&y[i*block_size], block_size);
3279 for (int ib=0; ib<block_size; ++ib)
3280 {
3281 yi[ib] = b[ib + P[i]*block_size];
3282 }
3283 for (int k=IB[i]; k<ID[i]; ++k)
3284 {
3285 int j = JB[k];
3286 const DenseMatrix &L_ij = AB(k);
3287 yj.SetDataAndSize(&y[j*block_size], block_size);
3288 // y_i = y_i - L_ij*y_j
3289 L_ij.AddMult_a(-1.0, yj, yi);
3290 }
3291 }
3292 // Backward substitution to solve Ux = y
3293 for (int i=nblockrows-1; i >= 0; --i)
3294 {
3295 xi.SetDataAndSize(&x[P[i]*block_size], block_size);
3296 for (int ib=0; ib<block_size; ++ib)
3297 {
3298 xi[ib] = y[ib + i*block_size];
3299 }
3300 for (int k=ID[i]+1; k<IB[i+1]; ++k)
3301 {
3302 int j = JB[k];
3303 const DenseMatrix &U_ij = AB(k);
3304 xj.SetDataAndSize(&x[P[j]*block_size], block_size);
3305 // x_i = x_i - U_ij*x_j
3306 U_ij.AddMult_a(-1.0, xj, xi);
3307 }
3308 LUFactors A_ii_inv(&DB(0,0,i), &ipiv[i*block_size]);
3309 // x_i = D_ii^{-1} x_i
3310 A_ii_inv.Solve(block_size, 1, xi.GetData());
3311 }
3312}
3313
3314
3316 int it, real_t norm, const Vector &r, bool final)
3317{
3318 if (!ess_dofs_list) { return; }
3319
3320 real_t bc_norm_squared = 0.0;
3321 r.HostRead();
3323 for (int i = 0; i < ess_dofs_list->Size(); i++)
3324 {
3325 const real_t r_entry = r((*ess_dofs_list)[i]);
3326 bc_norm_squared += r_entry*r_entry;
3327 }
3328 bool print = true;
3329#ifdef MFEM_USE_MPI
3330 MPI_Comm comm = iter_solver->GetComm();
3331 if (comm != MPI_COMM_NULL)
3332 {
3333 double glob_bc_norm_squared = 0.0;
3334 MPI_Reduce(&bc_norm_squared, &glob_bc_norm_squared, 1,
3336 MPI_SUM, 0, comm);
3337 bc_norm_squared = glob_bc_norm_squared;
3338 int rank;
3339 MPI_Comm_rank(comm, &rank);
3340 print = (rank == 0);
3341 }
3342#endif
3343 if ((it == 0 || final || bc_norm_squared > 0.0) && print)
3344 {
3345 mfem::out << " ResidualBCMonitor : b.c. residual norm = "
3346 << sqrt(bc_norm_squared) << endl;
3347 }
3348}
3349
3350
3351#ifdef MFEM_USE_SUITESPARSE
3352
3354{
3355 mat = NULL;
3356 Numeric = NULL;
3357 AI = AJ = NULL;
3358 if (!use_long_ints)
3359 {
3360 umfpack_di_defaults(Control);
3361 }
3362 else
3363 {
3364 umfpack_dl_defaults(Control);
3365 }
3366}
3367
3369{
3370 void *Symbolic;
3371
3372 if (Numeric)
3373 {
3374 if (!use_long_ints)
3375 {
3376 umfpack_di_free_numeric(&Numeric);
3377 }
3378 else
3379 {
3380 umfpack_dl_free_numeric(&Numeric);
3381 }
3382 }
3383
3384 mat = const_cast<SparseMatrix *>(dynamic_cast<const SparseMatrix *>(&op));
3385 MFEM_VERIFY(mat, "not a SparseMatrix");
3386
3387 // UMFPack requires that the column-indices in mat corresponding to each
3388 // row be sorted.
3389 // Generally, this will modify the ordering of the entries of mat.
3391
3392 height = mat->Height();
3393 width = mat->Width();
3394 MFEM_VERIFY(width == height, "not a square matrix");
3395
3396 const int * Ap = mat->HostReadI();
3397 const int * Ai = mat->HostReadJ();
3398 const real_t * Ax = mat->HostReadData();
3399
3400 if (!use_long_ints)
3401 {
3402 int status = umfpack_di_symbolic(width, width, Ap, Ai, Ax, &Symbolic,
3403 Control, Info);
3404 if (status < 0)
3405 {
3406 umfpack_di_report_info(Control, Info);
3407 umfpack_di_report_status(Control, status);
3408 mfem_error("UMFPackSolver::SetOperator :"
3409 " umfpack_di_symbolic() failed!");
3410 }
3411
3412 status = umfpack_di_numeric(Ap, Ai, Ax, Symbolic, &Numeric,
3413 Control, Info);
3414 if (status < 0)
3415 {
3416 umfpack_di_report_info(Control, Info);
3417 umfpack_di_report_status(Control, status);
3418 mfem_error("UMFPackSolver::SetOperator :"
3419 " umfpack_di_numeric() failed!");
3420 }
3421 umfpack_di_free_symbolic(&Symbolic);
3422 }
3423 else
3424 {
3425 SuiteSparse_long status;
3426
3427 delete [] AJ;
3428 delete [] AI;
3429 AI = new SuiteSparse_long[width + 1];
3430 AJ = new SuiteSparse_long[Ap[width]];
3431 for (int i = 0; i <= width; i++)
3432 {
3433 AI[i] = (SuiteSparse_long)(Ap[i]);
3434 }
3435 for (int i = 0; i < Ap[width]; i++)
3436 {
3437 AJ[i] = (SuiteSparse_long)(Ai[i]);
3438 }
3439
3440 status = umfpack_dl_symbolic(width, width, AI, AJ, Ax, &Symbolic,
3441 Control, Info);
3442 if (status < 0)
3443 {
3444 umfpack_dl_report_info(Control, Info);
3445 umfpack_dl_report_status(Control, status);
3446 mfem_error("UMFPackSolver::SetOperator :"
3447 " umfpack_dl_symbolic() failed!");
3448 }
3449
3450 status = umfpack_dl_numeric(AI, AJ, Ax, Symbolic, &Numeric,
3451 Control, Info);
3452 if (status < 0)
3453 {
3454 umfpack_dl_report_info(Control, Info);
3455 umfpack_dl_report_status(Control, status);
3456 mfem_error("UMFPackSolver::SetOperator :"
3457 " umfpack_dl_numeric() failed!");
3458 }
3459 umfpack_dl_free_symbolic(&Symbolic);
3460 }
3461}
3462
3463void UMFPackSolver::Mult(const Vector &b, Vector &x) const
3464{
3465 if (mat == NULL)
3466 mfem_error("UMFPackSolver::Mult : matrix is not set!"
3467 " Call SetOperator first!");
3468 b.HostRead();
3469 x.HostReadWrite();
3470 if (!use_long_ints)
3471 {
3472 int status =
3473 umfpack_di_solve(UMFPACK_At, mat->HostReadI(), mat->HostReadJ(),
3474 mat->HostReadData(), x.HostWrite(), b.HostRead(),
3475 Numeric, Control, Info);
3476 umfpack_di_report_info(Control, Info);
3477 if (status < 0)
3478 {
3479 umfpack_di_report_status(Control, status);
3480 mfem_error("UMFPackSolver::Mult : umfpack_di_solve() failed!");
3481 }
3482 }
3483 else
3484 {
3485 SuiteSparse_long status =
3486 umfpack_dl_solve(UMFPACK_At, AI, AJ, mat->HostReadData(),
3487 x.HostWrite(), b.HostRead(), Numeric, Control,
3488 Info);
3489 umfpack_dl_report_info(Control, Info);
3490 if (status < 0)
3491 {
3492 umfpack_dl_report_status(Control, status);
3493 mfem_error("UMFPackSolver::Mult : umfpack_dl_solve() failed!");
3494 }
3495 }
3496}
3497
3499{
3500 if (mat == NULL)
3501 mfem_error("UMFPackSolver::MultTranspose : matrix is not set!"
3502 " Call SetOperator first!");
3503 b.HostRead();
3504 x.HostReadWrite();
3505 if (!use_long_ints)
3506 {
3507 int status =
3508 umfpack_di_solve(UMFPACK_A, mat->HostReadI(), mat->HostReadJ(),
3509 mat->HostReadData(), x.HostWrite(), b.HostRead(),
3510 Numeric, Control, Info);
3511 umfpack_di_report_info(Control, Info);
3512 if (status < 0)
3513 {
3514 umfpack_di_report_status(Control, status);
3515 mfem_error("UMFPackSolver::MultTranspose :"
3516 " umfpack_di_solve() failed!");
3517 }
3518 }
3519 else
3520 {
3521 SuiteSparse_long status =
3522 umfpack_dl_solve(UMFPACK_A, AI, AJ, mat->HostReadData(),
3523 x.HostWrite(), b.HostRead(), Numeric, Control,
3524 Info);
3525 umfpack_dl_report_info(Control, Info);
3526 if (status < 0)
3527 {
3528 umfpack_dl_report_status(Control, status);
3529 mfem_error("UMFPackSolver::MultTranspose :"
3530 " umfpack_dl_solve() failed!");
3531 }
3532 }
3533}
3534
3536{
3537 delete [] AJ;
3538 delete [] AI;
3539 if (Numeric)
3540 {
3541 if (!use_long_ints)
3542 {
3543 umfpack_di_free_numeric(&Numeric);
3544 }
3545 else
3546 {
3547 umfpack_dl_free_numeric(&Numeric);
3548 }
3549 }
3550}
3551
3553{
3554 klu_defaults(&Common);
3555}
3556
3558{
3559 if (Numeric)
3560 {
3561 MFEM_VERIFY(Symbolic != 0,
3562 "Had Numeric pointer in KLU, but not Symbolic");
3563 klu_free_symbolic(&Symbolic, &Common);
3564 Symbolic = 0;
3565 klu_free_numeric(&Numeric, &Common);
3566 Numeric = 0;
3567 }
3568
3569 mat = const_cast<SparseMatrix *>(dynamic_cast<const SparseMatrix *>(&op));
3570 MFEM_VERIFY(mat != NULL, "not a SparseMatrix");
3571
3572 // KLU requires that the column-indices in mat corresponding to each row be
3573 // sorted. Generally, this will modify the ordering of the entries of mat.
3575
3576 height = mat->Height();
3577 width = mat->Width();
3578 MFEM_VERIFY(width == height, "not a square matrix");
3579
3580 int * Ap = mat->GetI();
3581 int * Ai = mat->GetJ();
3582 real_t * Ax = mat->GetData();
3583
3584 Symbolic = klu_analyze( height, Ap, Ai, &Common);
3585 Numeric = klu_factor(Ap, Ai, Ax, Symbolic, &Common);
3586}
3587
3588void KLUSolver::Mult(const Vector &b, Vector &x) const
3589{
3590 MFEM_VERIFY(mat != NULL,
3591 "KLUSolver::Mult : matrix is not set! Call SetOperator first!");
3592
3593 int n = mat->Height();
3594 int numRhs = 1;
3595 // Copy B into X, so we can pass it in and overwrite it.
3596 x = b;
3597 // Solve the transpose, since KLU thinks the matrix is compressed column
3598 // format.
3599 klu_tsolve( Symbolic, Numeric, n, numRhs, x.GetData(), &Common);
3600}
3601
3603{
3604 MFEM_VERIFY(mat != NULL,
3605 "KLUSolver::Mult : matrix is not set! Call SetOperator first!");
3606
3607 int n = mat->Height();
3608 int numRhs = 1;
3609 // Copy B into X, so we can pass it in and overwrite it.
3610 x = b;
3611 // Solve the regular matrix, not the transpose, since KLU thinks the matrix
3612 // is compressed column format.
3613 klu_solve( Symbolic, Numeric, n, numRhs, x.GetData(), &Common);
3614}
3615
3617{
3618 klu_free_symbolic (&Symbolic, &Common) ;
3619 klu_free_numeric (&Numeric, &Common) ;
3620 Symbolic = 0;
3621 Numeric = 0;
3622}
3623
3624#endif // MFEM_USE_SUITESPARSE
3625
3627 const SparseMatrix &block_dof_)
3628 : Solver(A.NumRows()), block_dof(const_cast<SparseMatrix&>(block_dof_)),
3629 block_solvers(new DenseMatrixInverse[block_dof.NumRows()])
3630{
3631 DenseMatrix sub_A;
3632 for (int i = 0; i < block_dof.NumRows(); ++i)
3633 {
3634 local_dofs.MakeRef(block_dof.GetRowColumns(i), block_dof.RowSize(i));
3635 sub_A.SetSize(local_dofs.Size());
3636 A.GetSubMatrix(local_dofs, local_dofs, sub_A);
3637 block_solvers[i].SetOperator(sub_A);
3638 }
3639}
3640
3642{
3643 y.SetSize(x.Size());
3644 y = 0.0;
3645
3646 for (int i = 0; i < block_dof.NumRows(); ++i)
3647 {
3648 local_dofs.MakeRef(block_dof.GetRowColumns(i), block_dof.RowSize(i));
3649 x.GetSubVector(local_dofs, sub_rhs);
3650 sub_sol.SetSize(local_dofs.Size());
3651 block_solvers[i].Mult(sub_rhs, sub_sol);
3652 y.AddElementVector(local_dofs, sub_sol);
3653 }
3654}
3655
3656void ProductSolver::Mult(const Vector & x, Vector & y) const
3657{
3658 y.SetSize(x.Size());
3659 y = 0.0;
3660 S0->Mult(x, y);
3661
3662 Vector z(x.Size());
3663 z = 0.0;
3664 A->Mult(y, z);
3665 add(-1.0, z, 1.0, x, z); // z = (I - A * S0) x
3666
3667 Vector S1z(x.Size());
3668 S1z = 0.0;
3669 S1->Mult(z, S1z);
3670 y += S1z;
3671}
3672
3674{
3675 y.SetSize(x.Size());
3676 y = 0.0;
3677 S1->MultTranspose(x, y);
3678
3679 Vector z(x.Size());
3680 z = 0.0;
3681 A->MultTranspose(y, z);
3682 add(-1.0, z, 1.0, x, z); // z = (I - A^T * S1^T) x
3683
3684 Vector S0Tz(x.Size());
3685 S0Tz = 0.0;
3686 S0->MultTranspose(z, S0Tz);
3687 y += S0Tz;
3688}
3689
3691 : Solver(0, false), global_size(-1)
3692#ifdef MFEM_USE_MPI
3693 , parallel(false)
3694#endif
3695{ }
3696
3697#ifdef MFEM_USE_MPI
3698OrthoSolver::OrthoSolver(MPI_Comm mycomm_)
3699 : Solver(0, false), mycomm(mycomm_), global_size(-1), parallel(true) { }
3700#endif
3701
3703{
3704 solver = &s;
3705 height = s.Height();
3706 width = s.Width();
3707 MFEM_VERIFY(height == width, "Solver must be a square Operator!");
3708 global_size = -1; // lazy evaluated
3709}
3710
3712{
3713 MFEM_VERIFY(solver, "Solver hasn't been set, call SetSolver() first.");
3714 solver->SetOperator(op);
3715 height = solver->Height();
3716 width = solver->Width();
3717 MFEM_VERIFY(height == width, "Solver must be a square Operator!");
3718 global_size = -1; // lazy evaluated
3719}
3720
3721void OrthoSolver::Mult(const Vector &b, Vector &x) const
3722{
3723 MFEM_VERIFY(solver, "Solver hasn't been set, call SetSolver() first.");
3724 MFEM_VERIFY(height == solver->Height(),
3725 "solver was modified externally! call SetSolver() again!");
3726 MFEM_VERIFY(height == b.Size(), "incompatible input Vector size!");
3727 MFEM_VERIFY(height == x.Size(), "incompatible output Vector size!");
3728
3729 // Orthogonalize input
3730 Orthogonalize(b, b_ortho);
3731
3732 // Propagate iterative_mode to the solver:
3734
3735 // Apply the Solver
3736 solver->Mult(b_ortho, x);
3737
3738 // Orthogonalize output
3739 Orthogonalize(x, x);
3740}
3741
3742void OrthoSolver::Orthogonalize(const Vector &v, Vector &v_ortho) const
3743{
3744 if (global_size == -1)
3745 {
3746 global_size = height;
3747#ifdef MFEM_USE_MPI
3748 if (parallel)
3749 {
3750 MPI_Allreduce(MPI_IN_PLACE, &global_size, 1, HYPRE_MPI_BIG_INT,
3751 MPI_SUM, mycomm);
3752 }
3753#endif
3754 }
3755
3756 // TODO: GPU/device implementation
3757
3758 real_t global_sum = v.Sum();
3759
3760#ifdef MFEM_USE_MPI
3761 if (parallel)
3762 {
3763 MPI_Allreduce(MPI_IN_PLACE, &global_sum, 1, MPITypeMap<real_t>::mpi_type,
3764 MPI_SUM, mycomm);
3765 }
3766#endif
3767
3768 real_t ratio = global_sum / static_cast<real_t>(global_size);
3769 v_ortho.SetSize(v.Size());
3770 v_ortho.UseDevice(true);
3771 v.HostRead();
3772 v_ortho.HostWrite();
3773 for (int i = 0; i < v_ortho.Size(); ++i)
3774 {
3775 v_ortho(i) = v(i) - ratio;
3776 }
3777}
3778
3779#ifdef MFEM_USE_MPI
3781 HypreParMatrix *aux_map,
3782 bool op_is_symmetric,
3783 bool own_aux_map)
3784 : Solver(op.NumRows()), aux_map_(aux_map, own_aux_map)
3785{
3786 aux_system_.Reset(RAP(&op, aux_map));
3787 aux_system_.As<HypreParMatrix>()->EliminateZeroRows();
3788 aux_smoother_.Reset(new HypreSmoother(*aux_system_.As<HypreParMatrix>()));
3789 aux_smoother_.As<HypreSmoother>()->SetOperatorSymmetry(op_is_symmetric);
3790}
3791
3792void AuxSpaceSmoother::Mult(const Vector &x, Vector &y, bool transpose) const
3793{
3794 Vector aux_rhs(aux_map_->NumCols());
3795 aux_map_->MultTranspose(x, aux_rhs);
3796
3797 Vector aux_sol(aux_rhs.Size());
3798 if (transpose)
3799 {
3800 aux_smoother_->MultTranspose(aux_rhs, aux_sol);
3801 }
3802 else
3803 {
3804 aux_smoother_->Mult(aux_rhs, aux_sol);
3805 }
3806
3807 y.SetSize(aux_map_->NumRows());
3808 aux_map_->Mult(aux_sol, y);
3809}
3810#endif // MFEM_USE_MPI
3811
3812#ifdef MFEM_USE_LAPACK
3813
3815 : Solver(0), mat(nullptr), const_tol_(1.0e-14), min_nnz_(0),
3816 max_nnz_(0), verbosity_(0), res_change_termination_tol_(1.0e-4),
3817 zero_tol_(1.0e-14), rhs_delta_(1.0e-11), n_outer_(100000),
3818 n_inner_(100000), nStallCheck_(100), normalize_(true),
3819 NNLS_qrres_on_(false), qr_residual_mode_(QRresidualMode::hybrid)
3820{}
3821
3823{
3824 mat = dynamic_cast<const DenseMatrix*>(&op);
3825 MFEM_VERIFY(mat, "NNLSSolver operator must be of type DenseMatrix");
3826
3827 // The size of this operator is that of the transpose of op.
3828 height = op.Width();
3829 width = op.Height();
3830
3831 row_scaling_.SetSize(mat->NumRows());
3832 row_scaling_.UseDevice(true);
3833 row_scaling_ = 1.0;
3834}
3835
3837{
3838 qr_residual_mode_ = qr_residual_mode;
3839 if (qr_residual_mode_ == QRresidualMode::on)
3840 {
3841 NNLS_qrres_on_ = true;
3842 }
3843}
3844
3846{
3847 // Scale everything so that rescaled half gap is the same for all constraints
3848 const int m = mat->NumRows();
3849
3850 MFEM_VERIFY(rhs_lb.Size() == m && rhs_ub.Size() == m, "");
3851
3852 Vector rhs_avg = rhs_ub;
3853 rhs_avg += rhs_lb;
3854 rhs_avg *= 0.5;
3855
3856 Vector rhs_halfgap = rhs_ub;
3857 rhs_halfgap -= rhs_lb;
3858 rhs_halfgap *= 0.5;
3859
3860 Vector rhs_avg_glob = rhs_avg;
3861 Vector rhs_halfgap_glob = rhs_halfgap;
3862 Vector halfgap_target(m);
3863 halfgap_target = 1.0e3 * const_tol_;
3864
3865 row_scaling_.SetSize(m);
3866 row_scaling_.UseDevice(true);
3867
3868 for (int i=0; i<m; ++i)
3869 {
3870 const real_t s = halfgap_target(i) / rhs_halfgap_glob(i);
3871 row_scaling_[i] = s;
3872
3873 rhs_lb(i) = (rhs_avg(i) * s) - halfgap_target(i);
3874 rhs_ub(i) = (rhs_avg(i) * s) + halfgap_target(i);
3875 }
3876}
3877
3878void NNLSSolver::Mult(const Vector &w, Vector &sol) const
3879{
3880 MFEM_VERIFY(mat, "NNLSSolver operator must be of type DenseMatrix");
3881 Vector rhs_ub(mat->NumRows());
3882 mat->Mult(w, rhs_ub);
3883 rhs_ub *= row_scaling_;
3884
3885 Vector rhs_lb(rhs_ub);
3886 Vector rhs_Gw(rhs_ub);
3887
3888 for (int i=0; i<rhs_ub.Size(); ++i)
3889 {
3890 rhs_lb(i) -= rhs_delta_;
3891 rhs_ub(i) += rhs_delta_;
3892 }
3893
3894 if (normalize_) { NormalizeConstraints(rhs_lb, rhs_ub); }
3895 Solve(rhs_lb, rhs_ub, sol);
3896
3897 if (verbosity_ > 1)
3898 {
3899 int nnz = 0;
3900 for (int i=0; i<sol.Size(); ++i)
3901 {
3902 if (sol(i) != 0.0)
3903 {
3904 nnz++;
3905 }
3906 }
3907
3908 mfem::out << "Number of nonzeros in NNLSSolver solution: " << nnz
3909 << ", out of " << sol.Size() << endl;
3910
3911 // Check residual of NNLS solution
3912 Vector res(mat->NumRows());
3913 mat->Mult(sol, res);
3914 res *= row_scaling_;
3915
3916 const real_t normGsol = res.Norml2();
3917 const real_t normRHS = rhs_Gw.Norml2();
3918
3919 res -= rhs_Gw;
3920 const real_t relNorm = res.Norml2() / std::max(normGsol, normRHS);
3921 mfem::out << "Relative residual norm for NNLSSolver solution of Gs = Gw: "
3922 << relNorm << endl;
3923 }
3924}
3925
3926void NNLSSolver::Solve(const Vector& rhs_lb, const Vector& rhs_ub,
3927 Vector& soln) const
3928{
3929 int m = mat->NumRows();
3930 int n = mat->NumCols();
3931
3932 MFEM_VERIFY(rhs_lb.Size() == m && rhs_lb.Size() == m && soln.Size() == n, "");
3933 MFEM_VERIFY(n >= m, "NNLSSolver system cannot be over-determined.");
3934
3935 if (max_nnz_ == 0)
3936 {
3937 max_nnz_ = mat->NumCols();
3938 }
3939
3940 // Prepare right hand side
3941 Vector rhs_avg(rhs_ub);
3942 rhs_avg += rhs_lb;
3943 rhs_avg *= 0.5;
3944
3945 Vector rhs_halfgap(rhs_ub);
3946 rhs_halfgap -= rhs_lb;
3947 rhs_halfgap *= 0.5;
3948
3949 Vector rhs_avg_glob(rhs_avg);
3950 Vector rhs_halfgap_glob(rhs_halfgap);
3951
3952 int ione = 1;
3953 real_t fone = 1.0;
3954
3955 char lside = 'L';
3956 char trans = 'T';
3957 char notrans = 'N';
3958
3959 std::vector<unsigned int> nz_ind(m);
3960 Vector res_glob(m);
3961 Vector mu(n);
3962 Vector mu2(n);
3963 int n_nz_ind = 0;
3964 int n_glob = 0;
3965 int m_update;
3966 int min_nnz_cap = std::min(static_cast<int>(min_nnz_), std::min(m,n));
3967 int info;
3968 std::vector<real_t> l2_res_hist;
3969 std::vector<unsigned int> stalled_indices;
3970 int stalledFlag = 0;
3971 int num_stalled = 0;
3972 int nz_ind_zero = 0;
3973
3974 Vector soln_nz_glob(m);
3975 Vector soln_nz_glob_up(m);
3976
3977 // The following matrices are stored in column-major format as Vectors
3978 Vector mat_0_data(m * n);
3979 Vector mat_qr_data(m * n);
3980 Vector submat_data(m * n);
3981
3982 Vector tau(n);
3983 Vector sub_tau = tau;
3984 Vector vec1(m);
3985
3986 // Temporary work arrays
3987 int lwork;
3988 std::vector<real_t> work;
3989 int n_outer_iter = 0;
3990 int n_total_inner_iter = 0;
3991 int i_qr_start;
3992 int n_update;
3993 // 0 = converged; 1 = maximum iterations reached;
3994 // 2 = NNLS stalled (no change in residual for many iterations)
3995 int exit_flag = 1;
3996
3997 res_glob = rhs_avg_glob;
3998 Vector qt_rhs_glob = rhs_avg_glob;
3999 Vector qqt_rhs_glob = qt_rhs_glob;
4000 Vector sub_qt = rhs_avg_glob;
4001
4002 // Compute threshold tolerance for the Lagrange multiplier mu
4003 real_t mu_tol = 0.0;
4004
4005 {
4006 Vector rhs_scaled(rhs_halfgap_glob);
4007 Vector tmp(n);
4008 rhs_scaled *= row_scaling_;
4009 mat->MultTranspose(rhs_scaled, tmp);
4010
4011 mu_tol = 1.0e-15 * tmp.Max();
4012 }
4013
4014 real_t rmax = 0.0;
4015 real_t mumax = 0.0;
4016
4017 for (int oiter = 0; oiter < n_outer_; ++oiter)
4018 {
4019 stalledFlag = 0;
4020
4021 rmax = fabs(res_glob(0)) - rhs_halfgap_glob(0);
4022 for (int i=1; i<m; ++i)
4023 {
4024 rmax = std::max(rmax, fabs(res_glob(i)) - rhs_halfgap_glob(i));
4025 }
4026
4027 l2_res_hist.push_back(res_glob.Norml2());
4028
4029 if (verbosity_ > 1)
4030 {
4031 mfem::out << "NNLS " << oiter << " " << n_total_inner_iter << " " << m
4032 << " " << n << " " << n_glob << " " << rmax << " "
4033 << l2_res_hist[oiter] << endl;
4034 }
4035 if (rmax <= const_tol_ && n_glob >= min_nnz_cap)
4036 {
4037 if (verbosity_ > 1)
4038 {
4039 mfem::out << "NNLS target tolerance met" << endl;
4040 }
4041 exit_flag = 0;
4042 break;
4043 }
4044
4045 if (n_glob >= max_nnz_)
4046 {
4047 if (verbosity_ > 1)
4048 {
4049 mfem::out << "NNLS target nnz met" << endl;
4050 }
4051 exit_flag = 0;
4052 break;
4053 }
4054
4055 if (n_glob >= m)
4056 {
4057 if (verbosity_ > 1)
4058 {
4059 mfem::out << "NNLS system is square... exiting" << endl;
4060 }
4061 exit_flag = 3;
4062 break;
4063 }
4064
4065 // Check for stall after the first nStallCheck iterations
4066 if (oiter > nStallCheck_)
4067 {
4068 real_t mean0 = 0.0;
4069 real_t mean1 = 0.0;
4070 for (int i=0; i<nStallCheck_/2; ++i)
4071 {
4072 mean0 += l2_res_hist[oiter - i];
4073 mean1 += l2_res_hist[oiter - (nStallCheck_) - i];
4074 }
4075
4076 real_t mean_res_change = (mean1 / mean0) - 1.0;
4077 if (std::abs(mean_res_change) < res_change_termination_tol_)
4078 {
4079 if (verbosity_ > 1)
4080 {
4081 mfem::out << "NNLSSolver stall detected... exiting" << endl;
4082 }
4083 exit_flag = 2;
4084 break;
4085 }
4086 }
4087
4088 // Find the next index
4089 res_glob *= row_scaling_;
4090 mat->MultTranspose(res_glob, mu);
4091
4092 for (int i = 0; i < n_nz_ind; ++i)
4093 {
4094 mu(nz_ind[i]) = 0.0;
4095 }
4096 for (unsigned int i = 0; i < stalled_indices.size(); ++i)
4097 {
4098 mu(stalled_indices[i]) = 0.0;
4099 }
4100
4101 mumax = mu.Max();
4102
4103 if (mumax < mu_tol)
4104 {
4105 num_stalled = stalled_indices.size();
4106 if (num_stalled > 0)
4107 {
4108 if (verbosity_ > 0)
4109 {
4110 mfem::out << "NNLS Lagrange multiplier is below the minimum "
4111 << "threshold: mumax = " << mumax << ", mutol = "
4112 << mu_tol << "\n" << " Resetting stalled indices "
4113 << "vector of size " << num_stalled << "\n";
4114 }
4115 stalled_indices.resize(0);
4116
4117 mat->MultTranspose(res_glob, mu);
4118
4119 for (int i = 0; i < n_nz_ind; ++i)
4120 {
4121 mu(nz_ind[i]) = 0.0;
4122 }
4123
4124 mumax = mu.Max();
4125 }
4126 }
4127
4128 int imax = 0;
4129 {
4130 real_t tmax = mu(0);
4131 for (int i=1; i<n; ++i)
4132 {
4133 if (mu(i) > tmax)
4134 {
4135 tmax = mu(i);
4136 imax = i;
4137 }
4138 }
4139 }
4140
4141 // Record the local value of the next index
4142 nz_ind[n_nz_ind] = imax;
4143 ++n_nz_ind;
4144
4145 if (verbosity_ > 2)
4146 {
4147 mfem::out << "Found next index: " << imax << " " << mumax << endl;
4148 }
4149
4150 for (int i=0; i<m; ++i)
4151 {
4152 mat_0_data(i + (n_glob*m)) = (*mat)(i,imax) * row_scaling_[i];
4153 mat_qr_data(i + (n_glob*m)) = mat_0_data(i + (n_glob*m));
4154 }
4155
4156 i_qr_start = n_glob;
4157 ++n_glob; // Increment the size of the global matrix
4158
4159 if (verbosity_ > 2)
4160 {
4161 mfem::out << "Updated matrix with new index" << endl;
4162 }
4163
4164 for (int iiter = 0; iiter < n_inner_; ++iiter)
4165 {
4166 ++n_total_inner_iter;
4167
4168 // Initialize
4169 const bool incremental_update = true;
4170 n_update = n_glob - i_qr_start;
4171 m_update = m - i_qr_start;
4172 if (incremental_update)
4173 {
4174 // Apply Householder reflectors to compute Q^T new_cols
4175 lwork = -1;
4176 work.resize(10);
4177
4178 MFEM_LAPACK_PREFIX(ormqr_)(&lside, &trans, &m, &n_update,
4179 &i_qr_start, mat_qr_data.GetData(), &m,
4180 tau.GetData(),
4181 mat_qr_data.GetData() + (i_qr_start * m),
4182 &m, work.data(), &lwork, &info);
4183 MFEM_VERIFY(info == 0, ""); // Q^T A update work calculation failed
4184 lwork = static_cast<int>(work[0]);
4185 work.resize(lwork);
4186 MFEM_LAPACK_PREFIX(ormqr_)(&lside, &trans, &m, &n_update,
4187 &i_qr_start, mat_qr_data.GetData(), &m,
4188 tau.GetData(),
4189 mat_qr_data.GetData() + (i_qr_start * m),
4190 &m, work.data(), &lwork, &info);
4191 MFEM_VERIFY(info == 0, ""); // Q^T A update failed
4192 // Compute QR factorization of the submatrix
4193 lwork = -1;
4194 work.resize(10);
4195
4196 // Copy m_update-by-n_update submatrix of mat_qr_data,
4197 // starting at (i_qr_start, i_qr_start)
4198 for (int i=0; i<m_update; ++i)
4199 for (int j=0; j<n_update; ++j)
4200 {
4201 submat_data[i + (j * m_update)] =
4202 mat_qr_data[i + i_qr_start + ((j + i_qr_start) * m)];
4203 }
4204
4205 // Copy tau subvector of length n_update, starting at i_qr_start
4206 for (int j=0; j<n_update; ++j)
4207 {
4208 sub_tau[j] = tau[i_qr_start + j];
4209 }
4210
4211 MFEM_LAPACK_PREFIX(geqrf_)(&m_update, &n_update, submat_data.GetData(),
4212 &m_update, sub_tau.GetData(), work.data(),
4213 &lwork, &info);
4214 MFEM_VERIFY(info == 0, ""); // QR update factorization work calc
4215 lwork = static_cast<int>(work[0]);
4216 if (lwork == 0) { lwork = 1; }
4217 work.resize(lwork);
4218 MFEM_LAPACK_PREFIX(geqrf_)(&m_update, &n_update, submat_data.GetData(),
4219 &m_update, sub_tau.GetData(), work.data(),
4220 &lwork, &info);
4221 MFEM_VERIFY(info == 0, ""); // QR update factorization failed
4222
4223 // Copy result back
4224 for (int i=0; i<m_update; ++i)
4225 for (int j=0; j<n_update; ++j)
4226 {
4227 mat_qr_data[i + i_qr_start + ((j + i_qr_start)* m)] =
4228 submat_data[i + (j * m_update)];
4229 }
4230
4231 for (int j=0; j<n_update; ++j)
4232 {
4233 tau[i_qr_start + j] = sub_tau[j];
4234 }
4235 }
4236 else
4237 {
4238 // Copy everything to mat_qr then do full QR
4239 for (int i=0; i<m; ++i)
4240 for (int j=0; j<n_glob; ++j)
4241 {
4242 mat_qr_data(i + (j*m)) = mat_0_data(i + (j*m));
4243 }
4244
4245 // Compute qr factorization (first find the size of work and then
4246 // perform qr)
4247 lwork = -1;
4248 work.resize(10);
4249 MFEM_LAPACK_PREFIX(geqrf_)(&m, &n_glob, mat_qr_data.GetData(), &m,
4250 tau.GetData(), work.data(), &lwork, &info);
4251 MFEM_VERIFY(info == 0, ""); // QR factorization work calculation
4252 lwork = static_cast<int>(work[0]);
4253 work.resize(lwork);
4254 MFEM_LAPACK_PREFIX(geqrf_)(&m, &n_glob, mat_qr_data.GetData(), &m,
4255 tau.GetData(), work.data(), &lwork, &info);
4256 MFEM_VERIFY(info == 0, ""); // QR factorization failed
4257 }
4258
4259 if (verbosity_ > 2)
4260 {
4261 mfem::out << "Updated QR " << iiter << endl;
4262 }
4263
4264 // Apply Householder reflectors to compute Q^T b
4265 if (incremental_update && iiter == 0)
4266 {
4267 lwork = -1;
4268 work.resize(10);
4269
4270 // Copy submatrix of mat_qr_data starting at
4271 // (i_qr_start, i_qr_start), of size m_update-by-1
4272 // Copy submatrix of qt_rhs_glob starting at (i_qr_start, 0),
4273 // of size m_update-by-1
4274
4275 for (int i=0; i<m_update; ++i)
4276 {
4277 submat_data[i] = mat_qr_data[i + i_qr_start + (i_qr_start * m)];
4278 sub_qt[i] = qt_rhs_glob[i + i_qr_start];
4279 }
4280
4281 sub_tau[0] = tau[i_qr_start];
4282
4283 MFEM_LAPACK_PREFIX(ormqr_)(&lside, &trans, &m_update, &ione, &ione,
4284 submat_data.GetData(), &m_update,
4285 sub_tau.GetData(), sub_qt.GetData(),
4286 &m_update, work.data(), &lwork, &info);
4287 MFEM_VERIFY(info == 0, ""); // H_last y work calculation failed
4288 lwork = static_cast<int>(work[0]);
4289 work.resize(lwork);
4290 MFEM_LAPACK_PREFIX(ormqr_)(&lside, &trans, &m_update, &ione, &ione,
4291 submat_data.GetData(), &m_update,
4292 sub_tau.GetData(), sub_qt.GetData(),
4293 &m_update, work.data(), &lwork, &info);
4294 MFEM_VERIFY(info == 0, ""); // H_last y failed
4295 // Copy result back
4296 for (int i=0; i<m_update; ++i)
4297 {
4298 qt_rhs_glob[i + i_qr_start] = sub_qt[i];
4299 }
4300 }
4301 else
4302 {
4303 // Compute Q^T b from scratch
4304 qt_rhs_glob = rhs_avg_glob;
4305 lwork = -1;
4306 work.resize(10);
4307 MFEM_LAPACK_PREFIX(ormqr_)(&lside, &trans, &m, &ione, &n_glob,
4308 mat_qr_data.GetData(), &m, tau.GetData(),
4309 qt_rhs_glob.GetData(), &m,
4310 work.data(), &lwork, &info);
4311 MFEM_VERIFY(info == 0, ""); // Q^T b work calculation failed
4312 lwork = static_cast<int>(work[0]);
4313 work.resize(lwork);
4314 MFEM_LAPACK_PREFIX(ormqr_)(&lside, &trans, &m, &ione, &n_glob,
4315 mat_qr_data.GetData(), &m, tau.GetData(),
4316 qt_rhs_glob.GetData(), &m,
4317 work.data(), &lwork, &info);
4318 MFEM_VERIFY(info == 0, ""); // Q^T b failed
4319 }
4320
4321 if (verbosity_ > 2)
4322 {
4323 mfem::out << "Updated rhs " << iiter << endl;
4324 }
4325
4326 // Apply R^{-1}; first n_glob entries of vec1 are overwritten
4327 char upper = 'U';
4328 char nounit = 'N';
4329 vec1 = qt_rhs_glob;
4330 MFEM_LAPACK_PREFIX(trsm_)(&lside, &upper, &notrans, &nounit,
4331 &n_glob, &ione, &fone,
4332 mat_qr_data.GetData(), &m,
4333 vec1.GetData(), &n_glob);
4334
4335 if (verbosity_ > 2)
4336 {
4337 mfem::out << "Solved triangular system " << iiter << endl;
4338 }
4339
4340 // Check if all entries are positive
4341 int pos_ibool = 0;
4342 real_t smin = n_glob > 0 ? vec1(0) : 0.0;
4343 for (int i=0; i<n_glob; ++i)
4344 {
4345 soln_nz_glob_up(i) = vec1(i);
4346 smin = std::min(smin, soln_nz_glob_up(i));
4347 }
4348
4349 if (smin > zero_tol_)
4350 {
4351 pos_ibool = 1;
4352 for (int i=0; i<n_glob; ++i)
4353 {
4354 soln_nz_glob(i) = soln_nz_glob_up(i);
4355 }
4356 }
4357
4358 if (pos_ibool == 1)
4359 {
4360 break;
4361 }
4362
4363 if (verbosity_ > 2)
4364 {
4365 mfem::out << "Start pruning " << iiter << endl;
4366 for (int i = 0; i < n_glob; ++i)
4367 {
4368 if (soln_nz_glob_up(i) <= zero_tol_)
4369 {
4370 mfem::out << i << " " << n_glob << " " << soln_nz_glob_up(i) << endl;
4371 }
4372 }
4373 }
4374
4375 if (soln_nz_glob_up(n_glob - 1) <= zero_tol_)
4376 {
4377 stalledFlag = 1;
4378 if (verbosity_ > 2)
4379 {
4380 if (qr_residual_mode_ == QRresidualMode::hybrid)
4381 {
4382 mfem::out << "Detected stall due to adding and removing same "
4383 << "column. Switching to QR residual calculation "
4384 << "method." << endl;
4385 }
4386 else
4387 {
4388 mfem::out << "Detected stall due to adding and removing same"
4389 << " column. Exiting now." << endl;
4390 }
4391 }
4392 }
4393
4394 if (stalledFlag == 1 && qr_residual_mode_ == QRresidualMode::hybrid)
4395 {
4396 NNLS_qrres_on_ = true;
4397 break;
4398 }
4399
4400 real_t alpha = numeric_limits<real_t>::max();
4401
4402 // Find maximum permissible step
4403 for (int i = 0; i < n_glob; ++i)
4404 {
4405 if (soln_nz_glob_up(i) <= zero_tol_)
4406 {
4407 alpha = std::min(alpha, soln_nz_glob(i)/(soln_nz_glob(i) - soln_nz_glob_up(i)));
4408 }
4409 }
4410 // Update solution
4411 smin = 0.0;
4412 for (int i = 0; i < n_glob; ++i)
4413 {
4414 soln_nz_glob(i) += alpha*(soln_nz_glob_up(i) - soln_nz_glob(i));
4415 if (i == 0 || soln_nz_glob(i) < smin)
4416 {
4417 smin = soln_nz_glob(i);
4418 }
4419 }
4420
4421 while (smin > zero_tol_)
4422 {
4423 // This means there was a rounding error, as we should have
4424 // a zero element by definition. Recalculate alpha based on
4425 // the index that corresponds to the element that should be
4426 // zero.
4427
4428 int index_min = 0;
4429 smin = soln_nz_glob(0);
4430 for (int i = 1; i < n_glob; ++i)
4431 {
4432 if (soln_nz_glob(i) < smin)
4433 {
4434 smin = soln_nz_glob(i);
4435 index_min = i;
4436 }
4437 }
4438
4439 alpha = soln_nz_glob(index_min)/(soln_nz_glob(index_min)
4440 - soln_nz_glob_up(index_min));
4441
4442 // Reupdate solution
4443 for (int i = 0; i < n_glob; ++i)
4444 {
4445 soln_nz_glob(i) += alpha*(soln_nz_glob_up(i) - soln_nz_glob(i));
4446 }
4447 }
4448
4449 // Clean up zeroed entry
4450 i_qr_start = n_glob+1;
4451 while (true)
4452 {
4453 // Check if there is a zero entry
4454 int zero_ibool;
4455
4456 smin = n_glob > 0 ? soln_nz_glob(0) : 0.0;
4457 for (int i=1; i<n_glob; ++i)
4458 {
4459 smin = std::min(smin, soln_nz_glob(i));
4460 }
4461
4462 if (smin < zero_tol_)
4463 {
4464 zero_ibool = 1;
4465 }
4466 else
4467 {
4468 zero_ibool = 0;
4469 }
4470
4471 if (zero_ibool == 0) // Break if there is no more zero entry
4472 {
4473 break;
4474 }
4475
4476 int ind_zero = -1; // Index where the first zero is encountered
4477 nz_ind_zero = 0;
4478
4479 // Identify global index of the zeroed element
4480 for (int i = 0; i < n_glob; ++i)
4481 {
4482 if (soln_nz_glob(i) < zero_tol_)
4483 {
4484 ind_zero = i;
4485 break;
4486 }
4487 }
4488 MFEM_VERIFY(ind_zero != -1, "");
4489 // Identify the local index for nz_ind to which the zeroed entry
4490 // belongs
4491 for (int i = 0; i < ind_zero; ++i)
4492 {
4493 ++nz_ind_zero;
4494 }
4495
4496 {
4497 // Copy mat_0.cols[ind_zero+1,n_glob) to mat_qr.cols[ind_zero,n_glob-1)
4498 for (int i=0; i<m; ++i)
4499 for (int j=ind_zero; j<n_glob-1; ++j)
4500 {
4501 mat_qr_data(i + (j*m)) = mat_0_data(i + ((j+1)*m));
4502 }
4503
4504 // Copy mat_qr.cols[ind_zero,n_glob-1) to
4505 // mat_0.cols[ind_zero,n_glob-1)
4506 for (int i=0; i<m; ++i)
4507 for (int j=ind_zero; j<n_glob-1; ++j)
4508 {
4509 mat_0_data(i + (j*m)) = mat_qr_data(i + (j*m));
4510 }
4511 }
4512
4513 // Remove the zeroed entry from the local matrix index
4514 for (int i = nz_ind_zero; i < n_nz_ind-1; ++i)
4515 {
4516 nz_ind[i] = nz_ind[i+1];
4517 }
4518 --n_nz_ind;
4519
4520 // Shift soln_nz_glob and proc_index
4521 for (int i = ind_zero; i < n_glob-1; ++i)
4522 {
4523 soln_nz_glob(i) = soln_nz_glob(i+1);
4524 }
4525
4526 i_qr_start = std::min(i_qr_start, ind_zero);
4527 --n_glob;
4528 } // End of pruning loop
4529
4530 if (verbosity_ > 2)
4531 {
4532 mfem::out << "Finished pruning " << iiter << endl;
4533 }
4534 } // End of inner loop
4535
4536 // Check if we have stalled
4537 if (stalledFlag == 1)
4538 {
4539 --n_glob;
4540 --n_nz_ind;
4541 num_stalled = stalled_indices.size();
4542 stalled_indices.resize(num_stalled + 1);
4543 stalled_indices[num_stalled] = imax;
4544 if (verbosity_ > 2)
4545 {
4546 mfem::out << "Adding index " << imax << " to stalled index list "
4547 << "of size " << num_stalled << endl;
4548 }
4549 }
4550
4551 // Compute residual
4552 if (!NNLS_qrres_on_)
4553 {
4554 res_glob = rhs_avg_glob;
4555 real_t fmone = -1.0;
4556 MFEM_LAPACK_PREFIX(gemv_)(&notrans, &m, &n_glob, &fmone,
4557 mat_0_data.GetData(), &m,
4558 soln_nz_glob.GetData(), &ione, &fone,
4559 res_glob.GetData(), &ione);
4560 }
4561 else
4562 {
4563 // Compute residual using res = b - Q*Q^T*b, where Q is from an
4564 // economical QR decomposition
4565 lwork = -1;
4566 work.resize(10);
4567 qqt_rhs_glob = 0.0;
4568 for (int i=0; i<n_glob; ++i)
4569 {
4570 qqt_rhs_glob(i) = qt_rhs_glob(i);
4571 }
4572
4573 MFEM_LAPACK_PREFIX(ormqr_)(&lside, &notrans, &m, &ione, &n_glob,
4574 mat_qr_data.GetData(), &m,
4575 tau.GetData(), qqt_rhs_glob.GetData(), &m,
4576 work.data(), &lwork, &info);
4577
4578 MFEM_VERIFY(info == 0, ""); // Q Q^T b work calculation failed.
4579 lwork = static_cast<int>(work[0]);
4580 work.resize(lwork);
4581 MFEM_LAPACK_PREFIX(ormqr_)(&lside, &notrans, &m, &ione, &n_glob,
4582 mat_qr_data.GetData(), &m,
4583 tau.GetData(), qqt_rhs_glob.GetData(), &m,
4584 work.data(), &lwork, &info);
4585 MFEM_VERIFY(info == 0, ""); // Q Q^T b calculation failed.
4586 res_glob = rhs_avg_glob;
4587 res_glob -= qqt_rhs_glob;
4588 }
4589
4590 if (verbosity_ > 2)
4591 {
4592 mfem::out << "Computed residual" << endl;
4593 }
4594
4595 ++n_outer_iter;
4596 } // End of outer loop
4597
4598 // Insert the solutions
4599 MFEM_VERIFY(n_glob == n_nz_ind, "");
4600 soln = 0.0;
4601 for (int i = 0; i < n_glob; ++i)
4602 {
4603 soln(nz_ind[i]) = soln_nz_glob(i);
4604 }
4605
4606 if (verbosity_ > 0)
4607 {
4608 mfem::out << "NNLS solver: m = " << m << ", n = " << n
4609 << ", outer_iter = " << n_outer_iter << ", inner_iter = "
4610 << n_total_inner_iter;
4611
4612 if (exit_flag == 0)
4613 {
4614 mfem::out << ": converged" << endl;
4615 }
4616 else
4617 {
4618 mfem::out << endl << "Warning, NNLS convergence stalled: "
4619 << (exit_flag == 2) << endl;
4620 mfem::out << "resErr = " << rmax << " vs tol = " << const_tol_
4621 << "; mumax = " << mumax << " vs tol = " << mu_tol << endl;
4622 }
4623 }
4624}
4625#endif // MFEM_USE_LAPACK
4626
4627}
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
Definition array.hpp:385
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:840
int Size() const
Return the logical size of the array.
Definition array.hpp:166
bool UseDevice() const
Return the device flag of the Memory object used by the Array.
Definition array.hpp:151
void MakeRef(T *data_, int size_, bool own_data=false)
Make this Array a reference to a pointer.
Definition array.hpp:1053
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:381
AuxSpaceSmoother(const HypreParMatrix &op, HypreParMatrix *aux_map, bool op_is_symmetric=true, bool own_aux_map=false)
Definition solvers.cpp:3780
BiCGSTAB method.
Definition solvers.hpp:710
void Mult(const Vector &b, Vector &x) const override
Iterative solution of the linear system using the BiCGSTAB method.
Definition solvers.cpp:1606
void SetOperator(const Operator &op) override
Set/update the solver for the given operator.
Definition solvers.hpp:723
A "square matrix" operator for the associated FE space and BLFIntegrators The sum of all the BLFInteg...
FiniteElementSpace * FESpace()
Return the FE space associated with the BilinearForm.
Reordering
The reordering method used by the BlockILU factorization.
Definition solvers.hpp:1108
BlockILU(int block_size_, Reordering reordering_=Reordering::MINIMUM_DISCARDED_FILL, int k_fill_=0)
Definition solvers.cpp:3015
void Mult(const Vector &b, Vector &x) const
Solve the system LUx = b, where L and U are the block ILU factors.
Definition solvers.cpp:3264
void SetOperator(const Operator &op)
Definition solvers.cpp:3033
Conjugate gradient method.
Definition solvers.hpp:627
void Mult(const Vector &b, Vector &x) const override
Iterative solution of the linear system using the Conjugate Gradient method.
Definition solvers.cpp:869
void SetOperator(const Operator &op) override
Set/update the solver for the given operator.
Definition solvers.hpp:640
void UpdateVectors()
Definition solvers.cpp:855
virtual real_t Eval(const Vector &x, const Vector &y) override
Compute the inner product (x,y) of vectors x and y, only on the constrained entries.
Definition solvers.cpp:220
Vector xr
List of constrained indices.
Definition solvers.hpp:350
virtual void Mult(const Vector &x, Vector &y) const override
Apply the constraint to vector x and return in y.
Definition solvers.cpp:239
void SetIndices(const Array< int > &list)
Set/update the list of constraint indices.
Definition solvers.cpp:211
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
void AddMult_a(real_t a, const Vector &x, Vector &y) const
y += a * A.x
Definition densemat.cpp:241
void Mult(const real_t *x, real_t *y) const
Matrix vector multiplication.
Definition densemat.cpp:108
void MultTranspose(const real_t *x, real_t *y) const
Multiply a vector with the transpose matrix.
Definition densemat.cpp:158
void SetSize(int s)
Change the size of the DenseMatrix to s x s.
Definition densemat.hpp:116
void Add(const real_t c, const DenseMatrix &A)
Adds the matrix A multiplied by the number c to the matrix.
Definition densemat.cpp:589
void SetSize(int i, int j, int k, MemoryType mt_=MemoryType::PRESERVE)
real_t * GetData(int k)
static MemoryClass GetMemoryClass()
(DEPRECATED) Equivalent to GetDeviceMemoryClass().
Definition device.hpp:289
void Mult(const Vector &x, Vector &y) const override
Direct solution of the block diagonal linear system.
Definition solvers.cpp:3641
DirectSubBlockSolver(const SparseMatrix &A, const SparseMatrix &block_dof)
Definition solvers.cpp:3626
void Mult(const Vector &b, Vector &x) const override
Iterative solution of the linear system using the FGMRES method.
Definition solvers.cpp:1343
virtual int GetTrueVSize() const
Return the number of vector true (conforming) dofs.
Definition fespace.hpp:829
GMRES method.
Definition solvers.hpp:661
void SetKDim(int dim)
Set the number of iteration to perform between restarts, default is 50.
Definition solvers.hpp:673
void Mult(const Vector &b, Vector &x) const override
Iterative solution of the linear system using the GMRES method.
Definition solvers.cpp:1134
Wrapper for hypre's ParCSR matrix class.
Definition hypre.hpp:419
void GetDiag(Vector &diag) const
Get the local diagonal of the matrix.
Definition hypre.cpp:1607
Parallel smoothers in hypre.
Definition hypre.hpp:1066
virtual real_t Eval(const Vector &x, const Vector &y)=0
Compute the inner product (x,y) of vectors x and y. This is an abstract method that must be implement...
virtual real_t Dot(const Vector &x, const Vector &y) const
Standard global/local inner product.
Definition operator.cpp:855
virtual void MonitorSolution(int it, real_t norm, const Vector &x, bool final)
Monitor the solution vector x.
Definition solvers.hpp:70
const class IterativeSolver * iter_solver
The last IterativeSolver to which this controller was attached.
Definition solvers.hpp:40
virtual void MonitorResidual(int it, real_t norm, const Vector &r, bool final)
Monitor the solution vector r.
Definition solvers.hpp:64
Abstract base class for iterative solver.
Definition solvers.hpp:91
real_t abs_tol
Absolute tolerance.
Definition solvers.hpp:183
InnerProductOperator * dot_oper
Definition solvers.hpp:148
real_t rel_tol
Relative tolerance.
Definition solvers.hpp:180
PrintLevel print_options
Output behavior for the iterative solver.
Definition solvers.hpp:163
real_t GetFinalNorm() const
Returns the final residual norm after termination of the solver during the last call to Mult().
Definition solvers.hpp:304
void SetOperator(const Operator &op) override
Also calls SetOperator for the preconditioner if there is one.
Definition solvers.cpp:184
virtual real_t Dot(const Vector &x, const Vector &y) const
Return the standard (l2, i.e., Euclidean) inner product of x and y.
Definition solvers.cpp:58
const Operator * oper
Definition solvers.hpp:145
void SetRelTol(real_t rtol)
Definition solvers.hpp:238
virtual void SetPreconditioner(Solver &pr)
This should be called before SetOperator.
Definition solvers.cpp:178
bool ControllerRequiresUpdate() const
Indicated if the controller requires an update of the solution.
Definition solvers.hpp:209
int print_level
(DEPRECATED) Legacy print level definition, which is left for compatibility with custom iterative sol...
Definition solvers.hpp:156
int GetNumIterations() const
Returns the number of iterations taken during the last call to Mult()
Definition solvers.hpp:289
virtual void SetPrintLevel(int print_lvl)
Legacy method to set the level of verbosity of the solver output.
Definition solvers.cpp:76
int max_iter
Limit for the number of iterations the solver is allowed to do.
Definition solvers.hpp:177
void SetMaxIter(int max_it)
Definition solvers.hpp:240
bool Monitor(int it, real_t norm, const Vector &r, const Vector &x, bool final=false) const
Monitor both the residual r and the solution x.
Definition solvers.cpp:195
IterativeSolverController * controller
Definition solvers.hpp:147
MPI_Comm GetComm() const
Return the associated MPI communicator, or MPI_COMM_NULL if no communicator is set.
Definition solvers.hpp:337
static int GuessLegacyPrintLevel(PrintLevel)
Use some heuristics to guess a legacy print level corresponding to the given PrintLevel.
Definition solvers.cpp:154
bool GetConverged() const
Returns true if the last call to Mult() converged successfully.
Definition solvers.hpp:291
PrintLevel FromLegacyPrintLevel(int)
Convert a legacy print level integer to a PrintLevel object.
Definition solvers.cpp:119
void SetAbsTol(real_t atol)
Definition solvers.hpp:239
real_t Norm(const Vector &x) const
Return the inner product norm of x, using the inner product defined by Dot()
Definition solvers.hpp:206
virtual ~KLUSolver()
Definition solvers.cpp:3616
void MultTranspose(const Vector &b, Vector &x) const override
Direct solution of the transposed linear system using KLU.
Definition solvers.cpp:3602
klu_symbolic * Symbolic
Definition solvers.hpp:1257
SparseMatrix * mat
Definition solvers.hpp:1256
klu_common Common
Definition solvers.hpp:1281
klu_numeric * Numeric
Definition solvers.hpp:1258
void Mult(const Vector &b, Vector &x) const override
Direct solution of the linear system using KLU.
Definition solvers.cpp:3588
void SetOperator(const Operator &op) override
Set/update the solver for the given operator.
Definition solvers.cpp:3557
Array< Vector * > skArray
Definition solvers.hpp:869
void Mult(const Vector &b, Vector &x) const override
Solve the nonlinear system with right-hand side b.
Definition solvers.cpp:2253
Array< Vector * > ykArray
Definition solvers.hpp:869
void Solve(int m, int n, real_t *X) const override
MINRES method.
Definition solvers.hpp:742
void Mult(const Vector &b, Vector &x) const override
Iterative solution of the linear system using the MINRES method.
Definition solvers.cpp:1855
void SetOperator(const Operator &op) override
Also calls SetOperator for the preconditioner if there is one.
Definition solvers.cpp:1830
void SetPreconditioner(Solver &pr) override
This should be called before SetOperator.
Definition solvers.hpp:754
void Solve(const Vector &rhs_lb, const Vector &rhs_ub, Vector &soln) const
Solve the NNLS problem. Specifically, we find a vector soln, such that rhs_lb < mat*soln < rhs_ub is ...
Definition solvers.cpp:3926
QRresidualMode
Enumerated types of QRresidual mode. Options are 'off': the residual is calculated normally,...
Definition solvers.hpp:1467
void Mult(const Vector &w, Vector &sol) const override
Compute the non-negative least squares solution to the underdetermined system.
Definition solvers.cpp:3878
void SetQRResidualMode(const QRresidualMode qr_residual_mode)
Set the residual calculation mode for the NNLS solver. See QRresidualMode enum above for details.
Definition solvers.cpp:3836
void SetOperator(const Operator &op) override
The operator must be a DenseMatrix.
Definition solvers.cpp:3822
void NormalizeConstraints(Vector &rhs_lb, Vector &rhs_ub) const
Normalize the constraints such that the tolerances for each constraint (i.e. (UB - LB)/2) are equal....
Definition solvers.cpp:3845
void SetAdaptiveLinRtol(const int type=2, const real_t rtol0=0.5, const real_t rtol_max=0.9, const real_t alpha=0.5 *(1.0+sqrt(5.0)), const real_t gamma=1.0)
Enable adaptive linear solver relative tolerance algorithm.
Definition solvers.cpp:2172
void AdaptiveLinRtolPreSolve(const Vector &x, const int it, const real_t fnorm) const
Method for the adaptive linear solver rtol invoked before the linear solve.
Definition solvers.cpp:2185
Operator * grad
Definition solvers.hpp:784
virtual real_t ComputeScalingFactor(const Vector &x, const Vector &b) const
This method can be overloaded in derived classes to implement line search algorithms.
Definition solvers.hpp:838
void Mult(const Vector &b, Vector &x) const override
Solve the nonlinear system with right-hand side b.
Definition solvers.cpp:2062
void AdaptiveLinRtolPostSolve(const Vector &x, const Vector &b, const int it, const real_t fnorm) const
Method for the adaptive linear solver rtol invoked after the linear solve.
Definition solvers.cpp:2232
virtual void ProcessNewState(const Vector &x) const
This method can be overloaded in derived classes to perform computations that need knowledge of the n...
Definition solvers.hpp:843
void SetOperator(const Operator &op) override
Also calls SetOperator for the preconditioner if there is one.
Definition solvers.cpp:2048
Chebyshev accelerated smoothing with given vector, no matrix necessary.
Definition solvers.hpp:503
void Mult(const Vector &x, Vector &y) const
Approach the solution of the linear system by applying Chebyshev smoothing.
Definition solvers.cpp:619
OperatorChebyshevSmoother(const Operator &oper_, const Vector &d, const Array< int > &ess_tdof_list, int order, real_t max_eig_estimate)
Definition solvers.cpp:455
OpType * As() const
Return the Operator pointer statically cast to a specified OpType. Similar to the method Get().
Definition handle.hpp:104
void Reset(OpType *A, bool own_A=true)
Reset the OperatorHandle to the given OpType pointer, A.
Definition handle.hpp:145
Jacobi smoothing for a given bilinear form (no matrix necessary).
Definition solvers.hpp:422
void Setup(const Vector &diag)
Definition solvers.cpp:401
OperatorJacobiSmoother(const real_t damping=1.0)
Default constructor: the diagonal will be computed by subsequent calls to SetOperator() using the Ope...
Definition solvers.cpp:324
void SetOperator(const Operator &op)
Recompute the diagonal using the method AssembleDiagonal of the given new Operator,...
Definition solvers.cpp:365
void Mult(const Vector &x, Vector &y) const
Approach the solution of the linear system by applying Jacobi smoothing.
Definition solvers.cpp:427
Abstract operator.
Definition operator.hpp:25
virtual MemoryClass GetMemoryClass() const
Return the MemoryClass preferred by the Operator.
Definition operator.hpp:86
int width
Dimension of the input / number of columns in the matrix.
Definition operator.hpp:28
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:66
int height
Dimension of the output / number of rows in the matrix.
Definition operator.hpp:27
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
int NumCols() const
Get the number of columns (size of input) of the Operator. Synonym with Width().
Definition operator.hpp:75
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition operator.hpp:72
virtual void AssembleDiagonal(Vector &diag) const
Computes the diagonal entries into diag. Typically, this operation only makes sense for linear Operat...
Definition operator.hpp:143
int NumRows() const
Get the number of rows (size of output) of the Operator. Synonym with Height().
Definition operator.hpp:69
virtual void MultTranspose(const Vector &x, Vector &y) const
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
Definition operator.hpp:100
virtual Operator & GetGradient(const Vector &x) const
Evaluate the gradient operator at the point x. The default behavior in class Operator is to generate ...
Definition operator.hpp:134
const Operator * C
Not owned, some can remain unused (NULL).
Definition solvers.hpp:967
void SetSolutionBounds(const Vector &xl, const Vector &xh)
Definition solvers.cpp:2599
void SetEqualityConstraint(const Vector &c)
Definition solvers.cpp:2581
int GetNumConstraints() const
Definition solvers.cpp:2607
const Operator * D
Definition solvers.hpp:967
OptimizationProblem(int insize, const Operator *C_, const Operator *D_)
In parallel, insize is the number of the local true dofs.
Definition solvers.cpp:2571
void SetInequalityConstraint(const Vector &dl, const Vector &dh)
Definition solvers.cpp:2589
const OptimizationProblem * problem
Definition solvers.hpp:1007
void SetOperator(const Operator &op) override
Set the Operator that is the OrthoSolver is to invert (approximately).
Definition solvers.cpp:3711
void SetSolver(Solver &s)
Set the solver used by the OrthoSolver.
Definition solvers.cpp:3702
void Mult(const Vector &b, Vector &x) const override
Perform the action of the OrthoSolver: P * solver * P where P is the projection to the subspace of ve...
Definition solvers.cpp:3721
PowerMethod helper class to estimate the largest eigenvalue of an operator using the iterative power ...
real_t EstimateLargestEigenvalue(Operator &opr, Vector &v0, int numSteps=10, real_t tolerance=1e-8, int seed=12345)
Returns an estimate of the largest eigenvalue of the operator opr using the iterative power method.
Definition operator.cpp:871
General product operator: x -> (A*B)(x) = A(B(x)).
Definition operator.hpp:906
void Mult(const Vector &x, Vector &y) const override
Solution of the linear system using a product of subsolvers.
Definition solvers.cpp:3656
void MultTranspose(const Vector &x, Vector &y) const override
Solution of the transposed linear system using a product of subsolvers.
Definition solvers.cpp:3673
void MonitorResidual(int it, real_t norm, const Vector &r, bool final) override
Monitor the solution vector r.
Definition solvers.cpp:3315
const Array< int > * ess_dofs_list
Not owned.
Definition solvers.hpp:1195
void Mult(const Vector &xt, Vector &x) const override
Definition solvers.cpp:2649
void SetBounds(const Vector &lo_, const Vector &hi_)
Definition solvers.cpp:2628
void SetLinearConstraint(const Vector &w_, real_t a_)
Definition solvers.cpp:2634
void print_iteration(int it, real_t r, real_t l) const
Definition solvers.cpp:2640
void SetOptimizationProblem(const OptimizationProblem &prob) override
Definition solvers.cpp:2615
real_t solve(real_t l, const Vector &xt, Vector &x, int &nclip) const
Solve QP at fixed lambda.
Definition solvers.hpp:1043
Stationary linear iteration: x <- x + B (b - A x)
Definition solvers.hpp:591
void Mult(const Vector &b, Vector &x) const override
Iterative solution of the linear system using Stationary Linear Iteration.
Definition solvers.cpp:669
void SetOperator(const Operator &op) override
Also calls SetOperator for the preconditioner if there is one.
Definition solvers.hpp:604
void UpdateVectors()
Definition solvers.cpp:660
Base class for solvers.
Definition operator.hpp:792
bool iterative_mode
If true, use the second argument of Mult() as an initial guess.
Definition operator.hpp:795
virtual void SetOperator(const Operator &op)=0
Set/update the solver for the given operator.
Data type sparse matrix.
Definition sparsemat.hpp:51
const int * HostReadJ() const
void GetDiag(Vector &d) const
Returns the Diagonal of A.
void MultTranspose(const Vector &x, Vector &y) const override
Multiply a vector with the transposed matrix. y = At * x.
void GetSubMatrix(const Array< int > &rows, const Array< int > &cols, DenseMatrix &subm) const
bool Finalized() const
Returns whether or not CSR format has been finalized.
const real_t * HostReadData() const
int * GetRowColumns(const int row)
Return a pointer to the column indices in a row.
int RowSize(const int i) const
Returns the number of elements in row i.
real_t * HostReadWriteData()
void Mult(const Vector &x, Vector &y) const override
Matrix vector multiplication.
real_t * GetData()
Return the element data, i.e. the array A.
void SortColumnIndices()
Sort the column indices corresponding to each row.
int * GetJ()
Return the array J.
int * GetI()
Return the array I.
const int * HostReadI() const
SuiteSparse_long * AJ
Definition solvers.hpp:1215
real_t Control[UMFPACK_CONTROL]
Definition solvers.hpp:1220
SparseMatrix * mat
Definition solvers.hpp:1213
void SetOperator(const Operator &op) override
Factorize the given Operator op which must be a SparseMatrix.
Definition solvers.cpp:3368
SuiteSparse_long * AI
Definition solvers.hpp:1215
void MultTranspose(const Vector &b, Vector &x) const override
Direct solution of the transposed linear system using UMFPACK.
Definition solvers.cpp:3498
real_t Info[UMFPACK_INFO]
Definition solvers.hpp:1221
void Mult(const Vector &b, Vector &x) const override
Direct solution of the linear system using UMFPACK.
Definition solvers.cpp:3463
virtual ~UMFPackSolver()
Definition solvers.cpp:3535
Vector data type.
Definition vector.hpp:82
virtual const real_t * HostRead() const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:524
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:520
virtual real_t * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:536
void SetDataAndSize(real_t *d, int s)
Set the Vector data and size.
Definition vector.hpp:191
void AddElementVector(const Array< int > &dofs, const Vector &elemvect)
Add elements of the elemvect Vector to the entries listed in dofs. Negative dof values cause the -dof...
Definition vector.cpp:785
real_t Norml2() const
Returns the l2 norm of the vector.
Definition vector.cpp:968
Vector & Set(const real_t a, const Vector &x)
(*this) = a * x
Definition vector.cpp:341
real_t Max() const
Returns the maximal element of the vector.
Definition vector.cpp:1200
int Size() const
Returns the size of the vector.
Definition vector.hpp:234
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition vector.hpp:145
real_t Sum() const
Return the sum of the vector entries.
Definition vector.cpp:1246
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:584
virtual real_t * HostWrite()
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:532
real_t * GetData() const
Return a pointer to the beginning of the Vector data.
Definition vector.hpp:243
virtual real_t * HostReadWrite()
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:540
void GetSubVector(const Array< int > &dofs, Vector &elemvect) const
Extract entries listed in dofs to the output Vector elemvect.
Definition vector.cpp:676
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:528
Vector & Add(const real_t a, const Vector &Va)
(*this) += a * Va
Definition vector.cpp:326
void MakeRef(Vector &base, int offset, int size)
Reset the Vector to be a reference to a sub-vector of base.
Definition vector.hpp:660
Vector wx
Weighting operator for y.
Definition solvers.hpp:387
virtual real_t Eval(const Vector &x, const Vector &y) override
Compute the inner product (Y(y),X(x)) of vectors x and y, weighted by the operators X and Y.
Definition solvers.cpp:283
MemoryClass mem_class
Weighted vectors.
Definition solvers.hpp:388
virtual void Mult(const Vector &x, Vector &y) const override
Apply the weighting operator to vector x and return in y=Y(X(x)).
Definition solvers.cpp:304
void SetOperator(Operator *X, Operator *Y)
Set/update the weighting operators (not owned) for each term.
Definition solvers.cpp:254
Operator * operY
Weighting operator for x.
Definition solvers.hpp:386
const real_t alpha
Definition ex15.cpp:369
real_t omega
Definition ex25.cpp:142
real_t mu
Definition ex25.cpp:140
prob_type prob
Definition ex25.cpp:156
void trans(const Vector &u, Vector &x)
Definition ex27.cpp:412
real_t b
Definition lissajous.cpp:42
real_t delta
Definition lissajous.cpp:43
real_t a
Definition lissajous.cpp:41
mfem::real_t real_t
MFEM_HOST_DEVICE dual< value_type, gradient_type > sqrt(dual< value_type, gradient_type > x)
implementation of square root for dual numbers
Definition dual.hpp:288
int BiCGSTAB(const Operator &A, Vector &x, const Vector &b, Solver &M, int &max_iter, real_t &tol, real_t atol, int printit)
BiCGSTAB method. (tolerances are squared)
Definition solvers.cpp:1807
void mfem_error(const char *msg)
Definition error.cpp:154
void AddMult_a(real_t alpha, const DenseMatrix &b, const DenseMatrix &c, DenseMatrix &a)
Matrix matrix multiplication. A += alpha * B * C.
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition globals.hpp:66
void add(const Vector &v1, const Vector &v2, Vector &v)
Definition vector.cpp:414
bool IsFinite(const real_t &val)
Definition vector.hpp:553
real_t InnerProduct(HypreParVector *x, HypreParVector *y)
Definition hypre.cpp:468
int GMRES(const Operator &A, Vector &x, const Vector &b, Solver &M, int &max_iter, int m, real_t &tol, real_t atol, int printit)
GMRES method. (tolerances are squared)
Definition solvers.cpp:1553
void ApplyPlaneRotation(real_t &dx, real_t &dy, real_t &cs, real_t &sn)
Definition solvers.cpp:1106
void RAP(const DenseMatrix &A, const DenseMatrix &P, DenseMatrix &RAP)
void Swap(T &a, T &b)
Swap objects of type T. The operation is performed using the most specialized swap function from the ...
Definition array.hpp:738
MemoryType GetMemoryType(MemoryClass mc)
Return a suitable MemoryType for a given MemoryClass.
int aGMRES(const Operator &A, Vector &x, const Vector &b, const Operator &M, int &max_iter, int m_max, int m_min, int m_step, real_t cf, real_t &tol, real_t &atol, int printit)
Definition solvers.cpp:2409
void SLI(const Operator &A, const Vector &b, Vector &x, int print_iter, int max_num_iter, real_t RTOLERANCE, real_t ATOLERANCE)
Stationary linear iteration. (tolerances are squared)
Definition solvers.cpp:823
void PCG(const Operator &A, Solver &B, const Vector &b, Vector &x, int print_iter, int max_num_iter, real_t RTOLERANCE, real_t ATOLERANCE)
Preconditioned conjugate gradient method. (tolerances are squared)
Definition solvers.cpp:1067
void CG(const Operator &A, const Vector &b, Vector &x, int print_iter, int max_num_iter, real_t RTOLERANCE, real_t ATOLERANCE)
Conjugate gradient method. (tolerances are squared)
Definition solvers.cpp:1052
void GeneratePlaneRotation(real_t &dx, real_t &dy, real_t &cs, real_t &sn)
Definition solvers.cpp:1084
void MINRES(const Operator &A, const Vector &b, Vector &x, int print_it, int max_it, real_t rtol, real_t atol)
MINRES method without preconditioner. (tolerances are squared)
Definition solvers.cpp:2020
void subtract(const Vector &x, const Vector &y, Vector &z)
Definition vector.cpp:570
float real_t
Definition config.hpp:46
MemoryType
Memory types supported by MFEM.
void MinimumDiscardedFillOrdering(SparseMatrix &C, Array< int > &p)
Definition solvers.cpp:2907
void forall(int N, lambda &&body)
Definition forall.hpp:839
void Update(Vector &x, int k, DenseMatrix &h, Vector &s, Array< Vector * > &v)
Definition solvers.cpp:1113
void Add(const DenseMatrix &A, const DenseMatrix &B, real_t alpha, DenseMatrix &C)
C = A + alpha*B.
real_t p(const Vector &x, real_t t)
MFEM_HOST_DEVICE real_t norm(const Complex &z)
MFEM_HOST_DEVICE real_t abs(const Complex &z)
Settings for the output behavior of the IterativeSolver.
Definition solvers.hpp:103
bool errors
If a fatal problem has been detected the failure will be reported to mfem::err.
Definition solvers.hpp:106
bool iterations
Detailed information about each iteration will be reported to mfem::out.
Definition solvers.hpp:112
bool warnings
If a non-fatal problem has been detected some context-specific information will be reported to mfem::...
Definition solvers.hpp:109
bool first_and_last
Information about the first and last iteration will be printed to mfem::out.
Definition solvers.hpp:118
bool summary
A summary of the solver process will be reported after the last iteration to mfem::out.
Definition solvers.hpp:115
Helper struct to convert a C++ type to an MPI type.