deal.II version GIT relicensing-6842-g793a97d2aa 2026-10-02 14:00:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
line_minimization.h
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2018 - 2024 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13#ifndef dealii_line_minimization_h
14#define dealii_line_minimization_h
15
16#include <deal.II/base/config.h>
17
22
24
25#include <algorithm>
26#include <fstream>
27#include <functional>
28#include <limits>
29#include <optional>
30#include <string>
31
32
34
39{
48 template <typename NumberType>
49 std::optional<NumberType>
50 quadratic_fit(const NumberType x_low,
51 const NumberType f_low,
52 const NumberType g_low,
53 const NumberType x_hi,
54 const NumberType f_hi);
55
65 template <typename NumberType>
66 std::optional<NumberType>
67 cubic_fit(const NumberType x_low,
68 const NumberType f_low,
69 const NumberType g_low,
70 const NumberType x_hi,
71 const NumberType f_hi,
72 const NumberType g_hi);
73
81 template <typename NumberType>
82 std::optional<NumberType>
83 cubic_fit_three_points(const NumberType x_low,
84 const NumberType f_low,
85 const NumberType g_low,
86 const NumberType x_hi,
87 const NumberType f_hi,
88 const NumberType x_rec,
89 const NumberType f_rec);
90
102 template <typename NumberType>
103 NumberType
104 poly_fit(const NumberType x_low,
105 const NumberType f_low,
106 const NumberType g_low,
107 const NumberType x_hi,
108 const NumberType f_hi,
109 const NumberType g_hi,
113 const std::pair<NumberType, NumberType> bounds);
114
119 template <typename NumberType>
120 NumberType
121 poly_fit_three_points(const NumberType x_low,
122 const NumberType f_low,
123 const NumberType g_low,
124 const NumberType x_hi,
125 const NumberType f_hi,
126 const NumberType g_hi,
130 const std::pair<NumberType, NumberType> bounds);
131
132
300 template <typename NumberType>
301 std::pair<NumberType, unsigned int>
303 const std::function<std::pair<NumberType, NumberType>(const NumberType x)>
304 &func,
305 const NumberType f0,
306 const NumberType g0,
307 const std::function<
308 NumberType(const NumberType x_low,
309 const NumberType f_low,
310 const NumberType g_low,
311 const NumberType x_hi,
312 const NumberType f_hi,
313 const NumberType g_hi,
317 const std::pair<NumberType, NumberType> bounds)> &interpolate,
318 const NumberType a1,
319 const NumberType eta = 0.9,
320 const NumberType mu = 0.01,
321 const NumberType a_max = std::numeric_limits<NumberType>::max(),
322 const unsigned int max_evaluations = 20,
323 const bool debug_output = false);
324
325
326 // ------------------- inline and template functions ----------------
327
328
329#ifndef DOXYGEN
330
331
332 template <typename NumberType>
333 std::optional<NumberType>
334 quadratic_fit(const NumberType x1,
335 const NumberType f1,
336 const NumberType g1,
337 const NumberType x2,
338 const NumberType f2)
339 {
340 Assert(x1 != x2, ExcMessage("Point are the same"));
341 const NumberType denom = (2. * g1 * x2 - 2. * g1 * x1 - 2. * f2 + 2. * f1);
342 if (denom == 0)
343 return {};
344 else
345 return (g1 * (x2 * x2 - x1 * x1) + 2. * (f1 - f2) * x1) / denom;
346 }
347
348
349
350 template <typename NumberType>
351 std::optional<NumberType>
352 cubic_fit(const NumberType x1,
353 const NumberType f1,
354 const NumberType g1,
355 const NumberType x2,
356 const NumberType f2,
357 const NumberType g2)
358 {
359 Assert(x1 != x2, ExcMessage("Points are the same"));
360 const NumberType beta1 = g1 + g2 - 3. * (f1 - f2) / (x1 - x2);
361 const NumberType s = beta1 * beta1 - g1 * g2;
362 if (s < 0)
363 return {};
364
365 const NumberType beta2 = std::sqrt(s);
366 const NumberType denom =
367 x1 < x2 ? g2 - g1 + 2. * beta2 : g1 - g2 + 2. * beta2;
368 if (denom == 0.)
369 return {};
370
371 return x1 < x2 ? x2 - (x2 - x1) * (g2 + beta2 - beta1) / denom :
372 x1 - (x1 - x2) * (g1 + beta2 - beta1) / denom;
373 }
374
375
376
377 template <typename NumberType>
378 std::optional<NumberType>
379 cubic_fit_three_points(const NumberType x1,
380 const NumberType f1,
381 const NumberType g1,
382 const NumberType x2,
383 const NumberType f2,
384 const NumberType x3,
385 const NumberType f3)
386 {
387 Assert(x1 != x2, ExcMessage("Points are the same"));
388 Assert(x1 != x3, ExcMessage("Points are the same"));
389 // f(x) = A *(x-x1)^3 + B*(x-x1)^2 + C*(x-x1) + D
390 // =>
391 // D = f1
392 // C = g1
393
394 // the rest is a system of 2 equations:
395
396 const NumberType x2_shift = x2 - x1;
397 const NumberType x3_shift = x3 - x1;
398 const NumberType r1 = f2 - f1 - g1 * x2_shift;
399 const NumberType r2 = f3 - f1 - g1 * x3_shift;
400 const NumberType denom =
401 Utilities::fixed_power<2>(x2_shift * x3_shift) * (x2_shift - x3_shift);
402 if (denom == 0.)
403 return {};
404
405 const NumberType A = (r1 * Utilities::fixed_power<2>(x3_shift) -
406 r2 * Utilities::fixed_power<2>(x2_shift)) /
407 denom;
408 const NumberType B = (r2 * Utilities::fixed_power<3>(x2_shift) -
409 r1 * Utilities::fixed_power<3>(x3_shift)) /
410 denom;
411 const NumberType &C = g1;
412
413 // now get the minimizer:
414 const NumberType radical = B * B - A * C * 3;
415 if (radical < 0)
416 return {};
417
418 return x1 + (-B + std::sqrt(radical)) / (A * 3);
419 }
420
421
422
423 template <typename NumberType>
424 NumberType
425 poly_fit(const NumberType x1,
426 const NumberType f1,
427 const NumberType g1,
428 const NumberType x2,
429 const NumberType f2,
430 const NumberType g2,
434 const std::pair<NumberType, NumberType> bounds)
435 {
436 Assert(bounds.first < bounds.second, ExcMessage("Incorrect bounds"));
437
438 // Similar to scipy implementation but we fit based on two points
439 // with their gradients and do bisection on bounds.
440 // https://github.com/scipy/scipy/blob/v1.0.0/scipy/optimize/linesearch.py#L555-L563
441
442 // First try cubic interpolation
443 std::optional<NumberType> res = cubic_fit(x1, f1, g1, x2, f2, g2);
444 if (res && *res >= bounds.first && *res <= bounds.second)
445 return *res;
446
447 // cubic either fails or outside of safe region, do quadratic:
448 res = quadratic_fit(x1, f1, g1, x2, f2);
449 if (res && *res >= bounds.first && *res <= bounds.second)
450 return *res;
451
452 // quadratic either failed or outside of safe region. Do bisection
453 // on safe region
454 return (bounds.first + bounds.second) * 0.5;
455 }
456
457
458
459 template <typename NumberType>
460 NumberType
461 poly_fit_three_points(const NumberType x1,
462 const NumberType f1,
463 const NumberType g1,
464 const NumberType x2,
465 const NumberType f2,
466 const NumberType /*g2*/,
469 const FiniteSizeHistory<NumberType> & /*g_rec*/,
470 const std::pair<NumberType, NumberType> bounds)
471 {
472 Assert(bounds.first < bounds.second, ExcMessage("Incorrect bounds"));
473 AssertDimension(x_rec.size(), f_rec.size());
474
475 // Same as scipy implementation where cubic fit is using 3 points
476 // https://github.com/scipy/scipy/blob/v1.0.0/scipy/optimize/linesearch.py#L555-L563
477
478 // First try cubic interpolation after first iteration
479 std::optional<NumberType> res =
480 x_rec.size() > 0 ?
481 cubic_fit_three_points(x1, f1, g1, x2, f2, x_rec[0], f_rec[0]) :
482 std::optional<NumberType>{};
483 if (res && *res >= bounds.first && *res <= bounds.second)
484 return *res;
485
486 // cubic either fails or outside of safe region, do quadratic:
487 res = quadratic_fit(x1, f1, g1, x2, f2);
488 if (res && *res >= bounds.first && *res <= bounds.second)
489 return *res;
490
491 // quadratic either failed or outside of safe region. Do bisection
492 // on safe region
493 return (bounds.first + bounds.second) * 0.5;
494 }
495
496
497
498 template <typename NumberType>
499 std::pair<NumberType, unsigned int>
501 const std::function<std::pair<NumberType, NumberType>(const NumberType x)>
502 &func,
503 const NumberType f0,
504 const NumberType g0,
505 const std::function<
506 NumberType(const NumberType x_low,
507 const NumberType f_low,
508 const NumberType g_low,
509 const NumberType x_hi,
510 const NumberType f_hi,
511 const NumberType g_hi,
515 const std::pair<NumberType, NumberType> bounds)> &choose,
516 const NumberType a1,
517 const NumberType eta,
518 const NumberType mu,
519 const NumberType a_max,
520 const unsigned int max_evaluations,
521 const bool debug_output)
522 {
523 // Note that scipy use dcsrch() from Minpack2 Fortran lib for line search
524 Assert(mu < 0.5 && mu > 0, ExcMessage("mu is not in (0,1/2)."));
525 Assert(eta < 1. && eta > mu, ExcMessage("eta is not in (mu,1)."));
526 Assert(a_max > 0, ExcMessage("max is not positive."));
527 Assert(a1 > 0 && a1 <= a_max, ExcMessage("a1 is not in (0,max]."));
528 Assert(g0 < 0, ExcMessage("Initial slope is not negative"));
529
530 // Growth parameter for bracketing phase:
531 // 1 < tau1
532 const NumberType tau1 = 9.;
533 // shrink parameters for sectioning phase to prevent ai from being
534 // arbitrary close to the extremes of the interval.
535 // 0 < tau2 < tau3 <= 1/2
536 // tau2 <= eta is advisable
537 const NumberType tau2 = 0.1; // bound for closeness to a_lo
538 const NumberType tau3 = 0.5; // bound for closeness to a_hi
539
540 const NumberType g0_abs = std::abs(g0);
541 const NumberType f_min = f0 + a_max * mu * g0;
542
543 // return True if the first Wolfe condition (sufficient decrease) is
544 // satisfied
545 const auto w1 = [&](const NumberType a, const NumberType f) {
546 return f <= f0 + a * mu * g0;
547 };
548
549 // return True if the second Wolfe condition (curvature condition) is
550 // satisfied
551 const auto w2 = [&](const NumberType g) {
552 return std::abs(g) <= eta * g0_abs;
553 };
554
555 // Bracketing phase (Algorithm 2.6.2): look for a non-trivial interval
556 // which is known to contain an interval of acceptable points.
557 // We adopt notation of Noceal.
558 const NumberType x = std::numeric_limits<NumberType>::signaling_NaN();
559 NumberType a_lo = x, f_lo = x, g_lo = x;
560 NumberType a_hi = x, f_hi = x, g_hi = x;
561 NumberType ai = x, fi = x, gi = x;
562
563 // count function calls in i:
564 unsigned int i = 0;
565 {
566 NumberType f_prev, g_prev, a_prev;
567 ai = a1;
568 f_prev = f0;
569 g_prev = g0;
570 a_prev = 0;
571
572 while (i < max_evaluations)
573 {
574 const auto fgi = func(ai);
575 fi = fgi.first;
576 gi = fgi.second;
577 ++i;
578
579 if (debug_output)
580 deallog << "Bracketing phase: " << i << std::endl
581 << ai << ' ' << fi << ' ' << gi << ' ' << w1(ai, fi) << ' '
582 << w2(gi) << ' ' << f_min << std::endl;
583
584 // first check if we can stop bracketing or the whole line search:
585 if (fi <= f_min || ai == a_max)
586 {
587 if (debug_output)
588 deallog << "Reached the maximum step size." << std::endl;
589 return std::make_pair(ai, i);
590 }
591
592 if (!w1(ai, fi) ||
593 (fi >= f_prev && i > 1)) // violate first Wolfe or not descending
594 {
595 a_lo = a_prev;
596 f_lo = f_prev;
597 g_lo = g_prev;
598
599 a_hi = ai;
600 f_hi = fi;
601 g_hi = gi;
602 break; // end bracketing
603 }
604
605 if (w2(gi)) // satisfies both Wolfe conditions
606 {
607 if (debug_output)
608 deallog << "Satisfied both Wolfe conditions during Bracketing."
609 << std::endl;
610
611 Assert(w1(ai, fi), ExcInternalError());
612 return std::make_pair(ai, i);
613 }
614
615 if (gi >= 0) // not descending
616 {
617 a_lo = ai;
618 f_lo = fi;
619 g_lo = gi;
620
621 a_hi = a_prev;
622 f_hi = f_prev;
623 g_hi = g_prev;
624 break; // end bracketing
625 }
626
627 // extrapolation step with the bounds
628 const auto bounds =
629 std::make_pair(2. * ai - a_prev,
630 std::min(a_max, ai + tau1 * (ai - a_prev)));
631
632 a_prev = ai;
633 f_prev = fi;
634 g_prev = gi;
635
636 // NOTE: Fletcher's 2.6.2 includes optional extrapolation, we
637 // simply take the upper bound
638 // Scipy increases by factor of two:
639 // https://github.com/scipy/scipy/blob/v1.0.0/scipy/optimize/linesearch.py#L447
640 ai = bounds.second;
641 }
642 }
643
645 i < max_evaluations,
647 "Could not find the initial bracket within the given number of iterations."));
648
649 // Check properties of the bracket (Theorem 3.2 in More and Thuente, 94
650 // and Eq. 2.6.3 in Fletcher 2013
651
652 // FIXME: these conditions are actually violated for Fig3 and a1=10^3 in
653 // More and Thorenton, 94.
654
655 /*
656 Assert((f_lo < f_hi) && w1(a_lo, f_lo), ExcInternalError());
657 Assert(((a_hi - a_lo) * g_lo < 0) && !w2(g_lo), ExcInternalError());
658 Assert((w1(a_hi, f_hi) || f_hi >= f_lo), ExcInternalError());
659 */
660
661 // keep short history of last points to improve interpolation
662 FiniteSizeHistory<NumberType> a_rec(5), f_rec(5), g_rec(5);
663 // if neither a_lo nor a_hi are zero:
664 if (std::abs(a_lo) > std::numeric_limits<NumberType>::epsilon() &&
665 std::abs(a_hi) > std::numeric_limits<NumberType>::epsilon())
666 {
667 a_rec.add(0);
668 f_rec.add(f0);
669 g_rec.add(g0);
670 }
671
672 // Now sectioning phase: we allow both [a_lo, a_hi] and [a_hi, a_lo]
673 while (i < max_evaluations)
674 {
675 const NumberType a_lo_safe = a_lo + tau2 * (a_hi - a_lo);
676 const NumberType a_hi_safe = a_hi - tau3 * (a_hi - a_lo);
677 const auto bounds = std::minmax(a_lo_safe, a_hi_safe);
678
679 ai = choose(
680 a_lo, f_lo, g_lo, a_hi, f_hi, g_hi, a_rec, f_rec, g_rec, bounds);
681
682 const std::pair<NumberType, NumberType> fgi = func(ai);
683 fi = fgi.first;
684 gi = fgi.second;
685 ++i;
686
687 if (debug_output)
688 deallog << "Sectioning phase: " << i << std::endl
689 << a_lo << ' ' << f_lo << ' ' << g_lo << ' ' << w1(a_lo, f_lo)
690 << ' ' << w2(g_lo) << std::endl
691 << a_hi << ' ' << f_hi << ' ' << g_hi << ' ' << w1(a_hi, f_hi)
692 << ' ' << w2(g_hi) << std::endl
693 << ai << ' ' << fi << ' ' << gi << ' ' << w1(ai, fi) << ' '
694 << w2(gi) << std::endl;
695
696 if (!w1(ai, fi) || fi >= f_lo)
697 // take [a_lo, ai]
698 {
699 a_rec.add(a_hi);
700 f_rec.add(f_hi);
701 g_rec.add(g_hi);
702
703 a_hi = ai;
704 f_hi = fi;
705 g_hi = gi;
706 }
707 else
708 {
709 if (w2(gi)) // satisfies both wolf
710 {
711 if (debug_output)
712 deallog << "Satisfied both Wolfe conditions." << std::endl;
713 Assert(w1(ai, fi), ExcInternalError());
714 return std::make_pair(ai, i);
715 }
716
717 if (gi * (a_hi - a_lo) >= 0)
718 // take [ai, a_lo]
719 {
720 a_rec.add(a_hi);
721 f_rec.add(f_hi);
722 g_rec.add(g_hi);
723
724 a_hi = a_lo;
725 f_hi = f_lo;
726 g_hi = g_lo;
727 }
728 else
729 // take [ai, a_hi]
730 {
731 a_rec.add(a_lo);
732 f_rec.add(f_lo);
733 g_rec.add(g_lo);
734 }
735
736 a_lo = ai;
737 f_lo = fi;
738 g_lo = gi;
739 }
740 }
741
742 // if we got here, we could not find the solution
744 false,
746 "Could not could complete the sectioning phase within the given number of iterations."));
747 return std::make_pair(std::numeric_limits<NumberType>::signaling_NaN(), i);
748 }
749
750#endif // DOXYGEN
751
752} // namespace LineMinimization
753
755
756#endif // dealii_line_minimization_h
std::size_t size() const
void add(const T &element)
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
LogStream deallog
Definition logstream.cc:36
constexpr char A
std::pair< NumberType, unsigned int > line_search(const std::function< std::pair< NumberType, NumberType >(const NumberType x)> &func, const NumberType f0, const NumberType g0, const std::function< NumberType(const NumberType x_low, const NumberType f_low, const NumberType g_low, const NumberType x_hi, const NumberType f_hi, const NumberType g_hi, const FiniteSizeHistory< NumberType > &x_rec, const FiniteSizeHistory< NumberType > &f_rec, const FiniteSizeHistory< NumberType > &g_rec, const std::pair< NumberType, NumberType > bounds)> &interpolate, const NumberType a1, const NumberType eta=0.9, const NumberType mu=0.01, const NumberType a_max=std::numeric_limits< NumberType >::max(), const unsigned int max_evaluations=20, const bool debug_output=false)
NumberType poly_fit(const NumberType x_low, const NumberType f_low, const NumberType g_low, const NumberType x_hi, const NumberType f_hi, const NumberType g_hi, const FiniteSizeHistory< NumberType > &x_rec, const FiniteSizeHistory< NumberType > &f_rec, const FiniteSizeHistory< NumberType > &g_rec, const std::pair< NumberType, NumberType > bounds)
NumberType poly_fit_three_points(const NumberType x_low, const NumberType f_low, const NumberType g_low, const NumberType x_hi, const NumberType f_hi, const NumberType g_hi, const FiniteSizeHistory< NumberType > &x_rec, const FiniteSizeHistory< NumberType > &f_rec, const FiniteSizeHistory< NumberType > &g_rec, const std::pair< NumberType, NumberType > bounds)
std::optional< NumberType > cubic_fit_three_points(const NumberType x_low, const NumberType f_low, const NumberType g_low, const NumberType x_hi, const NumberType f_hi, const NumberType x_rec, const NumberType f_rec)
std::optional< NumberType > cubic_fit(const NumberType x_low, const NumberType f_low, const NumberType g_low, const NumberType x_hi, const NumberType f_hi, const NumberType g_hi)
std::optional< NumberType > quadratic_fit(const NumberType x_low, const NumberType f_low, const NumberType g_low, const NumberType x_hi, const NumberType f_hi)
SymmetricTensor< 2, dim, Number > C(const Tensor< 2, dim, Number > &F)
*  *  *  ThermoPlasticMaterial< dim, ViscoplasticYieldLaw, Number >  ThermoPlasticMaterial *  mu(mu)
STL namespace.
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)