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
polynomial.cc
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) 2000 - 2026 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
15#include <deal.II/base/point.h>
20
21#include <algorithm>
22#include <cmath>
23#include <limits>
24#include <shared_mutex>
25
27
28
29
30namespace Polynomials
31{
32 // -------------------- class Polynomial ---------------- //
33
34
35 template <typename number>
36 Polynomial<number>::Polynomial(const std::vector<number> &a)
37 : coefficients(a)
38 , in_lagrange_product_form(false)
39 , lagrange_weight(1.)
40 {}
41
42
43
44 template <typename number>
45 Polynomial<number>::Polynomial(const unsigned int n)
46 : coefficients(n + 1, 0.)
47 , in_lagrange_product_form(false)
48 , lagrange_weight(1.)
49 {}
50
51
52
53 template <typename number>
54 Polynomial<number>::Polynomial(const std::vector<Point<1>> &supp,
55 const unsigned int center)
56 : in_lagrange_product_form(true)
57 {
58 Assert(supp.size() > 0, ExcEmptyObject());
59 AssertIndexRange(center, supp.size());
60
61 lagrange_support_points.reserve(supp.size() - 1);
62 number tmp_lagrange_weight = 1.;
63 for (unsigned int i = 0; i < supp.size(); ++i)
64 if (i != center)
65 {
66 lagrange_support_points.push_back(supp[i][0]);
67 tmp_lagrange_weight *= supp[center][0] - supp[i][0];
68 }
69
70 // check for underflow and overflow
71 Assert(std::fabs(tmp_lagrange_weight) > std::numeric_limits<number>::min(),
72 ExcMessage("Underflow in computation of Lagrange denominator."));
73 Assert(std::fabs(tmp_lagrange_weight) < std::numeric_limits<number>::max(),
74 ExcMessage("Overflow in computation of Lagrange denominator."));
75
76 lagrange_weight = static_cast<number>(1.) / tmp_lagrange_weight;
77 }
78
79
80
81 template <typename number>
82 void
83 Polynomial<number>::value(const number x, std::vector<number> &values) const
84 {
85 Assert(values.size() > 0, ExcZero());
86
87 value(x, values.size() - 1, values.data());
88 }
89
90
91
92 template <typename number>
93 void
95 {
96 // should only be called when the product form is active
97 Assert(in_lagrange_product_form == true, ExcInternalError());
98 Assert(coefficients.empty(), ExcInternalError());
99
100 // compute coefficients by expanding the product (x-x_i) term by term
101 coefficients.resize(lagrange_support_points.size() + 1);
102 if (lagrange_support_points.empty())
103 coefficients[0] = 1.;
104 else
105 {
106 coefficients[0] = -lagrange_support_points[0];
107 coefficients[1] = 1.;
108 for (unsigned int i = 1; i < lagrange_support_points.size(); ++i)
109 {
110 coefficients[i + 1] = 1.;
111 for (unsigned int j = i; j > 0; --j)
112 coefficients[j] = (-lagrange_support_points[i] * coefficients[j] +
113 coefficients[j - 1]);
114 coefficients[0] *= -lagrange_support_points[i];
115 }
116 }
117 for (unsigned int i = 0; i < lagrange_support_points.size() + 1; ++i)
118 coefficients[i] *= lagrange_weight;
119
120 // delete the product form data
121 std::vector<number> new_points;
122 lagrange_support_points.swap(new_points);
123 in_lagrange_product_form = false;
124 lagrange_weight = 1.;
125 }
126
127
128
129 template <typename number>
130 void
131 Polynomial<number>::scale(std::vector<number> &coefficients,
132 const number factor)
133 {
134 number f = 1.;
135 for (typename std::vector<number>::iterator c = coefficients.begin();
136 c != coefficients.end();
137 ++c)
138 {
139 *c *= f;
140 f *= factor;
141 }
142 }
143
144
145
146 template <typename number>
147 void
148 Polynomial<number>::scale(const number factor)
149 {
150 // to scale (x-x_0)*(x-x_1)*...*(x-x_n), scale
151 // support points by 1./factor and the weight
152 // likewise
153 if (in_lagrange_product_form == true)
154 {
155 number inv_fact = number(1.) / factor;
156 number accumulated_fact = 1.;
157 for (unsigned int i = 0; i < lagrange_support_points.size(); ++i)
158 {
159 lagrange_support_points[i] *= inv_fact;
160 accumulated_fact *= factor;
161 }
162 lagrange_weight *= accumulated_fact;
163 }
164 // otherwise, use the function above
165 else
166 scale(coefficients, factor);
167 }
168
169
170
171 template <typename number>
172 void
173 Polynomial<number>::multiply(std::vector<number> &coefficients,
174 const number factor)
175 {
176 for (typename std::vector<number>::iterator c = coefficients.begin();
177 c != coefficients.end();
178 ++c)
179 *c *= factor;
180 }
181
182
183
184 template <typename number>
187 {
188 if (in_lagrange_product_form == true)
189 lagrange_weight *= s;
190 else
191 {
192 for (typename std::vector<number>::iterator c = coefficients.begin();
193 c != coefficients.end();
194 ++c)
195 *c *= s;
196 }
197 return *this;
198 }
199
200
201
202 template <typename number>
205 {
206 // if we are in Lagrange form, just append the
207 // new points
208 if (in_lagrange_product_form == true && p.in_lagrange_product_form == true)
209 {
210 lagrange_weight *= p.lagrange_weight;
211 lagrange_support_points.insert(lagrange_support_points.end(),
212 p.lagrange_support_points.begin(),
214 }
215
216 // cannot retain product form, recompute...
217 else if (in_lagrange_product_form == true)
218 transform_into_standard_form();
219
220 // need to transform p into standard form as
221 // well if necessary. copy the polynomial to
222 // do this
223 std::unique_ptr<Polynomial<number>> q_data;
224 const Polynomial<number> *q = nullptr;
225 if (p.in_lagrange_product_form == true)
226 {
227 q_data = std::make_unique<Polynomial<number>>(p);
229 q = q_data.get();
230 }
231 else
232 q = &p;
233
234 // Degree of the product
235 unsigned int new_degree = this->degree() + q->degree();
236
237 std::vector<number> new_coefficients(new_degree + 1, 0.);
238
239 for (unsigned int i = 0; i < q->coefficients.size(); ++i)
240 for (unsigned int j = 0; j < this->coefficients.size(); ++j)
241 new_coefficients[i + j] += this->coefficients[j] * q->coefficients[i];
242 this->coefficients = std::move(new_coefficients);
243
244 return *this;
245 }
247
248
249 template <typename number>
253 // Lagrange product form cannot reasonably be
254 // retained after polynomial addition. we
255 // could in theory check if either this
256 // polynomial or the other is a zero
257 // polynomial and retain it, but we actually
258 // currently (r23974) assume that the addition
259 // of a zero polynomial changes the state and
260 // tests equivalence.
261 if (in_lagrange_product_form == true)
262 transform_into_standard_form();
263
264 // need to transform p into standard form as
265 // well if necessary. copy the polynomial to
266 // do this
267 std::unique_ptr<Polynomial<number>> q_data;
268 const Polynomial<number> *q = nullptr;
269 if (p.in_lagrange_product_form == true)
271 q_data = std::make_unique<Polynomial<number>>(p);
273 q = q_data.get();
274 }
275 else
276 q = &p;
277
278 // if necessary expand the number
279 // of coefficients we store
280 if (q->coefficients.size() > coefficients.size())
281 coefficients.resize(q->coefficients.size(), 0.);
282
283 for (unsigned int i = 0; i < q->coefficients.size(); ++i)
284 coefficients[i] += q->coefficients[i];
286 return *this;
287 }
288
289
290
291 template <typename number>
294 {
295 // Lagrange product form cannot reasonably be
296 // retained after polynomial addition
297 if (in_lagrange_product_form == true)
298 transform_into_standard_form();
300 // need to transform p into standard form as
301 // well if necessary. copy the polynomial to
302 // do this
303 std::unique_ptr<Polynomial<number>> q_data;
304 const Polynomial<number> *q = nullptr;
306 {
307 q_data = std::make_unique<Polynomial<number>>(p);
309 q = q_data.get();
310 }
311 else
312 q = &p;
314 // if necessary expand the number
315 // of coefficients we store
316 if (q->coefficients.size() > coefficients.size())
317 coefficients.resize(q->coefficients.size(), 0.);
318
319 for (unsigned int i = 0; i < q->coefficients.size(); ++i)
320 coefficients[i] -= q->coefficients[i];
321
322 return *this;
323 }
324
325
326
327 template <typename number>
328 bool
330 {
331 // need to distinguish a few cases based on
332 // whether we are in product form or not. two
333 // polynomials can still be the same when they
334 // are on different forms, but the expansion
335 // is the same
336 if (in_lagrange_product_form == true && p.in_lagrange_product_form == true)
337 return ((lagrange_weight == p.lagrange_weight) &&
338 (lagrange_support_points == p.lagrange_support_points));
339 else if (in_lagrange_product_form == true)
340 {
341 Polynomial<number> q = *this;
343 return (q.coefficients == p.coefficients);
344 }
345 else if (p.in_lagrange_product_form == true)
346 {
347 Polynomial<number> q = p;
349 return (q.coefficients == coefficients);
350 }
351 else
352 return (p.coefficients == coefficients);
353 }
354
355
356
357 template <typename number>
358 template <typename number2>
359 void
360 Polynomial<number>::shift(std::vector<number> &coefficients,
361 const number2 offset)
362 {
363 // too many coefficients cause overflow in
364 // the binomial coefficient used below
365 Assert(coefficients.size() < 31, ExcNotImplemented());
366
367 // Copy coefficients to a vector of
368 // accuracy given by the argument
369 std::vector<number2> new_coefficients(coefficients.begin(),
370 coefficients.end());
371
372 // Traverse all coefficients from
373 // c_1. c_0 will be modified by
374 // higher degrees, only.
375 for (unsigned int d = 1; d < new_coefficients.size(); ++d)
376 {
377 const unsigned int n = d;
378 // Binomial coefficients are
379 // needed for the
380 // computation. The rightmost
381 // value is unity.
382 unsigned int binomial_coefficient = 1;
383
384 // Powers of the offset will be
385 // needed and computed
386 // successively.
387 number2 offset_power = offset;
388
389 // Compute (x+offset)^d
390 // and modify all values c_k
391 // with k<d.
392 // The coefficient in front of
393 // x^d is not modified in this step.
394 for (unsigned int k = 0; k < d; ++k)
395 {
396 // Recursion from Bronstein
397 // Make sure no remainders
398 // occur in integer
399 // division.
400 binomial_coefficient = (binomial_coefficient * (n - k)) / (k + 1);
401
402 new_coefficients[d - k - 1] +=
403 new_coefficients[d] * binomial_coefficient * offset_power;
404 offset_power *= offset;
405 }
406 // The binomial coefficient
407 // should have gone through a
408 // whole row of Pascal's
409 // triangle.
410 Assert(binomial_coefficient == 1, ExcInternalError());
411 }
412
413 // copy new elements to old vector
414 coefficients.assign(new_coefficients.begin(), new_coefficients.end());
415 }
416
417
418
419 template <typename number>
420 template <typename number2>
421 void
422 Polynomial<number>::shift(const number2 offset)
423 {
424 // shift is simple for a polynomial in product
425 // form, (x-x_0)*(x-x_1)*...*(x-x_n). just add
426 // offset to all shifts
427 if (in_lagrange_product_form == true)
428 {
429 for (unsigned int i = 0; i < lagrange_support_points.size(); ++i)
430 lagrange_support_points[i] -= offset;
431 }
432 else
433 // do the shift in any case
434 shift(coefficients, offset);
435 }
436
437
438
439 template <typename number>
442 {
443 // no simple form possible for Lagrange
444 // polynomial on product form
445 if (degree() == 0)
446 return Monomial<number>(0, 0.);
447
448 std::unique_ptr<Polynomial<number>> q_data;
449 const Polynomial<number> *q = nullptr;
450 if (in_lagrange_product_form == true)
451 {
452 q_data = std::make_unique<Polynomial<number>>(*this);
454 q = q_data.get();
455 }
456 else
457 q = this;
458
459 std::vector<number> newcoefficients(q->coefficients.size() - 1);
460 for (unsigned int i = 1; i < q->coefficients.size(); ++i)
461 newcoefficients[i - 1] = number(i) * q->coefficients[i];
462
463 return Polynomial<number>(newcoefficients);
464 }
465
466
467
468 template <typename number>
471 {
472 // no simple form possible for Lagrange
473 // polynomial on product form
474 std::unique_ptr<Polynomial<number>> q_data;
475 const Polynomial<number> *q = nullptr;
476 if (in_lagrange_product_form == true)
477 {
478 q_data = std::make_unique<Polynomial<number>>(*this);
480 q = q_data.get();
481 }
482 else
483 q = this;
484
485 std::vector<number> newcoefficients(q->coefficients.size() + 1);
486 newcoefficients[0] = 0.;
487 for (unsigned int i = 0; i < q->coefficients.size(); ++i)
488 newcoefficients[i + 1] = q->coefficients[i] / number(i + 1.);
489
490 return Polynomial<number>(newcoefficients);
491 }
492
493
494
495 template <typename number>
496 void
497 Polynomial<number>::print(std::ostream &out) const
498 {
499 if (in_lagrange_product_form == true)
500 {
501 out << lagrange_weight;
502 for (unsigned int i = 0; i < lagrange_support_points.size(); ++i)
503 out << " (x-" << lagrange_support_points[i] << ")";
504 out << std::endl;
505 }
506 else
507 for (int i = degree(); i >= 0; --i)
508 {
509 out << coefficients[i] << " x^" << i << std::endl;
510 }
511 }
512
513
514 template <typename number>
515 std::size_t
517 {
518 return (MemoryConsumption::memory_consumption(coefficients) +
519 MemoryConsumption::memory_consumption(in_lagrange_product_form) +
520 MemoryConsumption::memory_consumption(lagrange_support_points) +
522 }
523
524
525
526 // ------------------ class Monomial -------------------------- //
527
528 template <typename number>
529 std::vector<Point<1>>
531 {
532 // Create vector of support points in 0 for representing the monomial as
533 // Lagrange polynomial, augmented by an additional 'node' point at 1. The
534 // latter point is added because of the way polynomials in Lagrange form
535 // are defined, taking one index 'i' among the given points as pivot (in
536 // our case, 0) to define a product (x - x[k]) / (x[i] - x[k]) for k
537 // running through the indices of the vector, excluding 'i'.
538 std::vector<Point<1>> vector(n + 1);
539 vector[0] = Point<1>(1.0);
540 return vector;
541 }
542
543
544
545 template <typename number>
546 Monomial<number>::Monomial(unsigned int n, double coefficient)
547 : Polynomial<number>(create_vector_of_roots(n), 0)
548 {
549 this->operator*=(coefficient);
550 }
551
552
553
554 template <typename number>
555 std::vector<Polynomial<number>>
557 {
558 std::vector<Polynomial<number>> v;
559 v.reserve(degree + 1);
560 for (unsigned int i = 0; i <= degree; ++i)
561 v.push_back(Monomial<number>(i));
562 return v;
563 }
564
565
566
567 // ------------------ class LagrangeEquidistant --------------- //
568
569 namespace internal
570 {
571 namespace LagrangeEquidistantImplementation
572 {
573 std::vector<Point<1>>
575 {
576 std::vector<Point<1>> points(n + 1);
577 const double one_over_n = 1. / n;
578 for (unsigned int k = 0; k <= n; ++k)
579 points[k][0] = static_cast<double>(k) * one_over_n;
580 return points;
581 }
582 } // namespace LagrangeEquidistantImplementation
583 } // namespace internal
584
585
586
588 const unsigned int support_point)
589 : Polynomial<double>(internal::LagrangeEquidistantImplementation::
590 generate_equidistant_unit_points(n),
591 support_point)
592 {
594
595 // For polynomial order up to 3, we have precomputed weights. Use these
596 // weights instead of the product form
597 if (n <= 3)
598 {
599 this->in_lagrange_product_form = false;
600 this->lagrange_weight = 1.;
601 std::vector<double> new_support_points;
602 this->lagrange_support_points.swap(new_support_points);
603 this->coefficients.resize(n + 1);
604 compute_coefficients(n, support_point, this->coefficients);
605 }
606 }
607
608
609
610 void
612 const unsigned int support_point,
613 std::vector<double> &a)
614 {
615 AssertIndexRange(support_point, n + 1);
616
617 unsigned int n_functions = n + 1;
618 AssertIndexRange(support_point, n_functions);
619 const double *x = nullptr;
620
621 switch (n)
622 {
623 case 1:
624 {
625 static const double x1[4] = {1.0, -1.0, 0.0, 1.0};
626 x = &x1[0];
627 break;
628 }
629 case 2:
630 {
631 static const double x2[9] = {
632 1.0, -3.0, 2.0, 0.0, 4.0, -4.0, 0.0, -1.0, 2.0};
633 x = &x2[0];
634 break;
635 }
636 case 3:
637 {
638 static const double x3[16] = {1.0,
639 -11.0 / 2.0,
640 9.0,
641 -9.0 / 2.0,
642 0.0,
643 9.0,
644 -45.0 / 2.0,
645 27.0 / 2.0,
646 0.0,
647 -9.0 / 2.0,
648 18.0,
649 -27.0 / 2.0,
650 0.0,
651 1.0,
652 -9.0 / 2.0,
653 9.0 / 2.0};
654 x = &x3[0];
655 break;
656 }
657 default:
659 }
660
661 Assert(x != nullptr, ExcInternalError());
662 for (unsigned int i = 0; i < n_functions; ++i)
663 a[i] = x[support_point * n_functions + i];
664 }
665
666
667
668 std::vector<Polynomial<double>>
670 {
671 if (degree == 0)
672 // create constant polynomial
673 return std::vector<Polynomial<double>>(
674 1, Polynomial<double>(std::vector<double>(1, 1.)));
675 else
676 {
677 // create array of Lagrange
678 // polynomials
679 std::vector<Polynomial<double>> v;
680 for (unsigned int i = 0; i <= degree; ++i)
681 v.push_back(LagrangeEquidistant(degree, i));
682 return v;
683 }
684 }
685
686
687
688 //----------------------------------------------------------------------//
689
690
691 std::vector<Polynomial<double>>
692 generate_complete_Lagrange_basis(const std::vector<Point<1>> &points)
693 {
694 std::vector<Polynomial<double>> p;
695 p.reserve(points.size());
696
697 for (unsigned int i = 0; i < points.size(); ++i)
698 p.emplace_back(points, i);
699 return p;
700 }
701
702
703
704 // ------------------ class Legendre --------------- //
705
706
707
708 Legendre::Legendre(const unsigned int k)
709 : Polynomial<double>(0)
710 {
711 this->coefficients.clear();
712 this->in_lagrange_product_form = true;
713 this->lagrange_support_points.resize(k);
714
715 // the roots of a Legendre polynomial are exactly the points in the
716 // Gauss-Legendre quadrature formula
717 if (k > 0)
718 {
719 const QGauss<1> gauss(k);
720 for (unsigned int i = 0; i < k; ++i)
721 this->lagrange_support_points[i] = gauss.get_points()[i][0];
722 }
723
724 // compute the abscissa in zero of the product of monomials. The exact
725 // value should be sqrt(2*k+1), so set the weight to that value.
726 double prod = 1.;
727 for (unsigned int i = 0; i < k; ++i)
728 prod *= this->lagrange_support_points[i];
729 this->lagrange_weight = std::sqrt(double(2 * k + 1)) / prod;
730 }
731
732
733
734 std::vector<Polynomial<double>>
735 Legendre::generate_complete_basis(const unsigned int degree)
736 {
737 std::vector<Polynomial<double>> v;
738 v.reserve(degree + 1);
739 for (unsigned int i = 0; i <= degree; ++i)
740 v.push_back(Legendre(i));
741 return v;
742 }
743
744
745
746 // ------------------ class Lobatto -------------------- //
747
748
749 Lobatto::Lobatto(const unsigned int p)
750 : Polynomial<double>(compute_coefficients(p))
751 {}
752
753 std::vector<double>
754 Lobatto::compute_coefficients(const unsigned int p)
755 {
756 switch (p)
757 {
758 case 0:
759 {
760 std::vector<double> coefficients(2);
761
762 coefficients[0] = 1.0;
763 coefficients[1] = -1.0;
764 return coefficients;
765 }
766
767 case 1:
768 {
769 std::vector<double> coefficients(2);
770
771 coefficients[0] = 0.0;
772 coefficients[1] = 1.0;
773 return coefficients;
774 }
775
776 case 2:
777 {
778 std::vector<double> coefficients(3);
779
780 coefficients[0] = 0.0;
781 coefficients[1] = -1.0 * std::sqrt(3.);
782 coefficients[2] = std::sqrt(3.);
783 return coefficients;
784 }
785
786 default:
787 {
788 std::vector<double> coefficients(p + 1);
789 std::vector<double> legendre_coefficients_tmp1(p);
790 std::vector<double> legendre_coefficients_tmp2(p - 1);
791
792 coefficients[0] = -1.0 * std::sqrt(3.);
793 coefficients[1] = 2.0 * std::sqrt(3.);
794 legendre_coefficients_tmp1[0] = 1.0;
795
796 for (unsigned int i = 2; i < p; ++i)
797 {
798 for (unsigned int j = 0; j < i - 1; ++j)
799 legendre_coefficients_tmp2[j] = legendre_coefficients_tmp1[j];
800
801 for (unsigned int j = 0; j < i; ++j)
802 legendre_coefficients_tmp1[j] = coefficients[j];
803
804 coefficients[0] =
805 std::sqrt(2 * i + 1.) *
806 ((1.0 - 2 * i) * legendre_coefficients_tmp1[0] /
807 std::sqrt(2 * i - 1.) +
808 (1.0 - i) * legendre_coefficients_tmp2[0] /
809 std::sqrt(2 * i - 3.)) /
810 i;
811
812 for (unsigned int j = 1; j < i - 1; ++j)
813 coefficients[j] =
814 std::sqrt(2 * i + 1.) *
815 (std::sqrt(2 * i - 1.) *
816 (2.0 * legendre_coefficients_tmp1[j - 1] -
817 legendre_coefficients_tmp1[j]) +
818 (1.0 - i) * legendre_coefficients_tmp2[j] /
819 std::sqrt(2 * i - 3.)) /
820 i;
821
822 coefficients[i - 1] = std::sqrt(4 * i * i - 1.) *
823 (2.0 * legendre_coefficients_tmp1[i - 2] -
824 legendre_coefficients_tmp1[i - 1]) /
825 i;
826 coefficients[i] = 2.0 * std::sqrt(4 * i * i - 1.) *
827 legendre_coefficients_tmp1[i - 1] / i;
828 }
829
830 for (int i = p; i > 0; --i)
831 coefficients[i] = coefficients[i - 1] / i;
832
833 coefficients[0] = 0.0;
834 return coefficients;
835 }
836 }
837 }
838
839 std::vector<Polynomial<double>>
841 {
842 std::vector<Polynomial<double>> basis(p + 1);
843
844 for (unsigned int i = 0; i <= p; ++i)
845 basis[i] = Lobatto(i);
846
847 return basis;
848 }
849
850
851
852 // ------------------ class Hierarchical --------------- //
853
854 // Reserve space for polynomials up to degree 19. Should be sufficient
855 // for the start.
856 std::vector<std::unique_ptr<const std::vector<double>>>
858
859 std::shared_mutex Hierarchical::coefficients_lock;
860
861
862 Hierarchical::Hierarchical(const unsigned int k)
863 : Polynomial<double>(get_coefficients(k))
864 {}
865
866
867
868 void
870 {
871 unsigned int k = k_;
872 // The first 2 coefficients
873 // are hard-coded
874 if (k == 0)
875 k = 1;
876
877 // First see whether the coefficients we need have already been
878 // computed. This is a read operation, and so we can do that
879 // with a shared lock.
880 //
881 // (We could have gotten away without any lock at all if the
882 // inner pointers were std::atomic<std::unique_ptr<...>>, but
883 // first, there is no such specialization of std::atomic that
884 // is mutex-free, and then there is also the issue that the
885 // outer vector may be resized and that can definitely not
886 // be guarded against without a mutex of some sort.)
887 {
888 std::shared_lock<std::shared_mutex> lock(coefficients_lock);
889
890 if ((recursive_coefficients.size() >= k + 1) &&
891 (recursive_coefficients[k].get() != nullptr))
892 return;
893 }
894
895 // Having gotten here, we know that we need to compute a new set
896 // of coefficients. This has to happen under a unique lock because
897 // we're not only reading, but writing into the data structures:
898 std::unique_lock<std::shared_mutex> lock(coefficients_lock);
899
900 // First make sure that there is enough
901 // space in the array for the
902 // coefficients, so we have to resize
903 // it to size k+1
904
905 // but it's more complicated than
906 // that: we call this function
907 // recursively, so if we simply
908 // resize it to k+1 here, then
909 // compute the coefficients for
910 // degree k-1 by calling this
911 // function recursively, then it will
912 // reset the size to k -- not enough
913 // for what we want to do below. the
914 // solution therefore is to only
915 // resize the size if we are going to
916 // *increase* it
917 if (recursive_coefficients.size() < k + 1)
918 recursive_coefficients.resize(k + 1);
919
920 if (k <= 1)
921 {
922 // create coefficients
923 // vectors for k=0 and k=1
924 //
925 // allocate the respective
926 // amount of memory and
927 // later assign it to the
928 // coefficients array to
929 // make it const
930 std::vector<double> c0(2);
931 c0[0] = 1.;
932 c0[1] = -1.;
933
934 std::vector<double> c1(2);
935 c1[0] = 0.;
936 c1[1] = 1.;
937
938 // now make these arrays
939 // const
941 std::make_unique<const std::vector<double>>(std::move(c0));
943 std::make_unique<const std::vector<double>>(std::move(c1));
944 }
945 else if (k == 2)
946 {
947 coefficients_lock.unlock();
949 coefficients_lock.lock();
950
951 std::vector<double> c2(3);
952
953 const double a = 1.; // 1./8.;
954
955 c2[0] = 0. * a;
956 c2[1] = -4. * a;
957 c2[2] = 4. * a;
958
960 std::make_unique<const std::vector<double>>(std::move(c2));
961 }
962 else
963 {
964 // for larger numbers,
965 // compute the coefficients
966 // recursively. to do so,
967 // we have to release the
968 // lock temporarily to
969 // allow the called
970 // function to acquire it
971 // itself
972 coefficients_lock.unlock();
974 coefficients_lock.lock();
975
976 std::vector<double> ck(k + 1);
977
978 const double a = 1.; // 1./(2.*k);
979
980 ck[0] = -a * (*recursive_coefficients[k - 1])[0];
981
982 for (unsigned int i = 1; i <= k - 1; ++i)
983 ck[i] = a * (2. * (*recursive_coefficients[k - 1])[i - 1] -
984 (*recursive_coefficients[k - 1])[i]);
985
986 ck[k] = a * 2. * (*recursive_coefficients[k - 1])[k - 1];
987 // for even degrees, we need
988 // to add a multiple of
989 // basis fcn phi_2
990 if ((k % 2) == 0)
991 {
992 double b = 1.; // 8.;
993 // for (unsigned int i=1; i<=k; ++i)
994 // b /= 2.*i;
995
996 ck[1] += b * (*recursive_coefficients[2])[1];
997 ck[2] += b * (*recursive_coefficients[2])[2];
998 }
999 // finally assign the newly
1000 // created vector to the
1001 // const pointer in the
1002 // coefficients array
1004 std::make_unique<const std::vector<double>>(std::move(ck));
1005 }
1006 }
1007
1008
1009
1010 const std::vector<double> &
1012 {
1013 // First make sure the coefficients get computed if so necessary
1015
1016 // Then get a pointer to the array of coefficients. Do that in a MT
1017 // safe way, but since we're only reading information we can do
1018 // that with a shared lock
1019 std::shared_lock<std::shared_mutex> lock(coefficients_lock);
1020 return *recursive_coefficients[k];
1021 }
1022
1023
1024
1025 std::vector<Polynomial<double>>
1026 Hierarchical::generate_complete_basis(const unsigned int degree)
1027 {
1028 if (degree == 0)
1029 // create constant
1030 // polynomial. note that we
1031 // can't use the other branch
1032 // of the if-statement, since
1033 // calling the constructor of
1034 // this class with argument
1035 // zero does _not_ create the
1036 // constant polynomial, but
1037 // rather 1-x
1038 return std::vector<Polynomial<double>>(
1039 1, Polynomial<double>(std::vector<double>(1, 1.)));
1040 else
1041 {
1042 std::vector<Polynomial<double>> v;
1043 v.reserve(degree + 1);
1044 for (unsigned int i = 0; i <= degree; ++i)
1045 v.push_back(Hierarchical(i));
1046 return v;
1047 }
1048 }
1049
1050
1051
1052 // ------------------ HermiteInterpolation --------------- //
1053
1055 : Polynomial<double>(0)
1056 {
1057 this->coefficients.clear();
1058 this->in_lagrange_product_form = true;
1059
1060 this->lagrange_support_points.resize(3);
1061 if (p == 0)
1062 {
1063 this->lagrange_support_points[0] = -0.5;
1064 this->lagrange_support_points[1] = 1.;
1065 this->lagrange_support_points[2] = 1.;
1066 this->lagrange_weight = 2.;
1067 }
1068 else if (p == 1)
1069 {
1070 this->lagrange_support_points[0] = 0.;
1071 this->lagrange_support_points[1] = 0.;
1072 this->lagrange_support_points[2] = 1.5;
1073 this->lagrange_weight = -2.;
1074 }
1075 else if (p == 2)
1076 {
1077 this->lagrange_support_points[0] = 0.;
1078 this->lagrange_support_points[1] = 1.;
1079 this->lagrange_support_points[2] = 1.;
1080 }
1081 else if (p == 3)
1082 {
1083 this->lagrange_support_points[0] = 0.;
1084 this->lagrange_support_points[1] = 0.;
1085 this->lagrange_support_points[2] = 1.;
1086 }
1087 else
1088 {
1089 this->lagrange_support_points.resize(4);
1090 this->lagrange_support_points[0] = 0.;
1091 this->lagrange_support_points[1] = 0.;
1092 this->lagrange_support_points[2] = 1.;
1093 this->lagrange_support_points[3] = 1.;
1094 this->lagrange_weight = 16.;
1095
1096 if (p > 4)
1097 {
1098 Legendre legendre(p - 4);
1099 (*this) *= legendre;
1100 }
1101 }
1102 }
1103
1104
1105 std::vector<Polynomial<double>>
1107 {
1108 Assert(n >= 3,
1109 ExcNotImplemented("Hermite interpolation makes no sense for "
1110 "degrees less than three"));
1111 std::vector<Polynomial<double>> basis(n + 1);
1112
1113 for (unsigned int i = 0; i <= n; ++i)
1114 basis[i] = HermiteInterpolation(i);
1115
1116 return basis;
1117 }
1118
1119
1120 // ------------------ HermiteLikeInterpolation --------------- //
1121 namespace
1122 {
1123 // Finds the zero position x_star such that the mass matrix entry (0,1)
1124 // with the Hermite polynomials evaluates to zero. The function has
1125 // originally been derived by a secant method for the integral entry
1126 // l_0(x) * l_1(x) but we only need to do one iteration because the zero
1127 // x_star is linear in the integral value.
1128 double
1129 find_support_point_x_star(const std::vector<double> &jacobi_roots)
1130 {
1131 // Initial guess for the support point position values: The zero turns
1132 // out to be between zero and the first root of the Jacobi polynomial,
1133 // but the algorithm is agnostic about that, so simply choose two points
1134 // that are sufficiently far apart.
1135 double guess_left = 0;
1136 double guess_right = 0.5;
1137 const unsigned int degree = jacobi_roots.size() + 3;
1138
1139 // Compute two integrals of the product of l_0(x) * l_1(x)
1140 // l_0(x) =
1141 // (x-y)*(x-jacobi_roots(0))*...*(x-jacobi_roos(degree-4))*(x-1)*(x-1)
1142 // l_1(x) =
1143 // (x-0)*(x-jacobi_roots(0))*...*(x-jacobi_roots(degree-4))*(x-1)*(x-1)
1144 // where y is either guess_left or guess_right for the two integrals.
1145 // Note that the polynomials are not yet normalized here, which is not
1146 // necessary because we are only looking for the x_star where the matrix
1147 // entry is zero, for which the constants do not matter.
1148 const QGauss<1> gauss(degree + 1);
1149 double integral_left = 0, integral_right = 0;
1150 for (unsigned int q = 0; q < gauss.size(); ++q)
1151 {
1152 const double x = gauss.point(q)[0];
1153 double poly_val_common = x;
1154 for (unsigned int j = 0; j < degree - 3; ++j)
1155 poly_val_common *= Utilities::fixed_power<2>(x - jacobi_roots[j]);
1156 poly_val_common *= Utilities::fixed_power<4>(x - 1.);
1157 integral_left +=
1158 gauss.weight(q) * (poly_val_common * (x - guess_left));
1159 integral_right +=
1160 gauss.weight(q) * (poly_val_common * (x - guess_right));
1161 }
1162
1163 // compute guess by secant method. Due to linearity in the root x_star,
1164 // this is the correct position after this single step
1165 return guess_right - (guess_right - guess_left) /
1166 (integral_right - integral_left) * integral_right;
1167 }
1168 } // namespace
1169
1170
1171
1173 const unsigned int index)
1174 : Polynomial<double>(0)
1175 {
1176 AssertIndexRange(index, degree + 1);
1177
1178 this->coefficients.clear();
1179 this->in_lagrange_product_form = true;
1180
1181 this->lagrange_support_points.resize(degree);
1182
1183 if (degree == 0)
1184 this->lagrange_weight = 1.;
1185 else if (degree == 1)
1186 {
1187 if (index == 0)
1188 {
1189 this->lagrange_support_points[0] = 1.;
1190 this->lagrange_weight = -1.;
1191 }
1192 else
1193 {
1194 this->lagrange_support_points[0] = 0.;
1195 this->lagrange_weight = 1.;
1196 }
1197 }
1198 else if (degree == 2)
1199 {
1200 if (index == 0)
1201 {
1202 this->lagrange_support_points[0] = 1.;
1203 this->lagrange_support_points[1] = 1.;
1204 this->lagrange_weight = 1.;
1205 }
1206 else if (index == 1)
1207 {
1208 this->lagrange_support_points[0] = 0;
1209 this->lagrange_support_points[1] = 1;
1210 this->lagrange_weight = -2.;
1211 }
1212 else
1213 {
1214 this->lagrange_support_points[0] = 0.;
1215 this->lagrange_support_points[1] = 0.;
1216 this->lagrange_weight = 1.;
1217 }
1218 }
1219 else if (degree == 3)
1220 {
1221 // 4 Polynomials with degree 3
1222 // entries (1,0) and (3,2) of the mass matrix will be equal to 0
1223 //
1224 // | x 0 x x |
1225 // | 0 x x x |
1226 // M = | x x x 0 |
1227 // | x x 0 x |
1228 //
1229 if (index == 0)
1230 {
1231 this->lagrange_support_points[0] = 2. / 7.;
1232 this->lagrange_support_points[1] = 1.;
1233 this->lagrange_support_points[2] = 1.;
1234 this->lagrange_weight = -3.5;
1235 }
1236 else if (index == 1)
1237 {
1238 this->lagrange_support_points[0] = 0.;
1239 this->lagrange_support_points[1] = 1.;
1240 this->lagrange_support_points[2] = 1.;
1241
1242 // this magic value 5.5 is obtained when evaluating the general
1243 // formula below for the degree=3 case
1244 this->lagrange_weight = 5.5;
1245 }
1246 else if (index == 2)
1247 {
1248 this->lagrange_support_points[0] = 0.;
1249 this->lagrange_support_points[1] = 0.;
1250 this->lagrange_support_points[2] = 1.;
1251 this->lagrange_weight = -5.5;
1252 }
1253 else if (index == 3)
1254 {
1255 this->lagrange_support_points[0] = 0.;
1256 this->lagrange_support_points[1] = 0.;
1257 this->lagrange_support_points[2] = 5. / 7.;
1258 this->lagrange_weight = 3.5;
1259 }
1260 }
1261 else
1262 {
1263 // Higher order Polynomials degree>=4: the entries (1,0) and
1264 // (degree,degree-1) of the mass matrix will be equal to 0
1265 //
1266 // | x 0 x x x x x |
1267 // | 0 x x x . . . x x x |
1268 // | x x x 0 0 x x |
1269 // | x x 0 x 0 x x |
1270 // | . . . |
1271 // M = | . . . |
1272 // | . . . |
1273 // | x x 0 0 x x x |
1274 // | x x x x . . . x x 0 |
1275 // | x x x x x 0 x |
1276 //
1277 // We find the inner points as the zeros of the Jacobi polynomials
1278 // with alpha = beta = 4 which is the polynomial with the kernel
1279 // (1-x)^4 (1+x)^4. Since polynomials (1-x)^2 (1+x)^2 are contained
1280 // in every interior polynomial (bubble function), their product
1281 // leads us to the orthogonality condition of the Jacobi(4,4)
1282 // polynomials.
1283
1284 std::vector<double> jacobi_roots =
1285 jacobi_polynomial_roots<double>(degree - 3, 4, 4);
1286 AssertDimension(jacobi_roots.size(), degree - 3);
1287
1288 // iteration from variable support point N with secant method
1289 // initial values
1290
1291 this->lagrange_support_points.resize(degree);
1292 if (index == 0)
1293 {
1294 const double auxiliary_zero =
1295 find_support_point_x_star(jacobi_roots);
1296 this->lagrange_support_points[0] = auxiliary_zero;
1297 for (unsigned int m = 0; m < degree - 3; ++m)
1298 this->lagrange_support_points[m + 1] = jacobi_roots[m];
1299 this->lagrange_support_points[degree - 2] = 1.;
1300 this->lagrange_support_points[degree - 1] = 1.;
1301
1302 // ensure that the polynomial evaluates to one at x=0
1303 this->lagrange_weight = 1. / this->value(0);
1304 }
1305 else if (index == 1)
1306 {
1307 this->lagrange_support_points[0] = 0.;
1308 for (unsigned int m = 0; m < degree - 3; ++m)
1309 this->lagrange_support_points[m + 1] = jacobi_roots[m];
1310 this->lagrange_support_points[degree - 2] = 1.;
1311 this->lagrange_support_points[degree - 1] = 1.;
1312
1313 // Select the weight to make the derivative of the sum of P_0 and
1314 // P_1 in zero to be 0. The derivative in x=0 is simply given by
1315 // p~(0)/auxiliary_zero+p~'(0) + a*p~(0), where p~(x) is the
1316 // Lagrange polynomial in all points except the first one which is
1317 // the same for P_0 and P_1, and a is the weight we seek here. If
1318 // we solve this for a, we obtain the desired property. Since the
1319 // basis is nodal for all interior points, this property ensures
1320 // that the sum of all polynomials with weight 1 is one.
1321 std::vector<Point<1>> points(degree);
1322 double ratio = 1.;
1323 for (unsigned int i = 0; i < degree; ++i)
1324 {
1325 points[i][0] = this->lagrange_support_points[i];
1326 if (i > 0)
1327 ratio *= -this->lagrange_support_points[i];
1328 }
1329 Polynomial<double> helper(points, 0);
1330 std::vector<double> value_and_grad(2);
1331 helper.value(0., value_and_grad);
1332 Assert(std::abs(value_and_grad[0]) > 1e-10,
1333 ExcInternalError("There should not be a zero at x=0."));
1334
1335 const double auxiliary_zero =
1336 find_support_point_x_star(jacobi_roots);
1337 this->lagrange_weight =
1338 (1. / auxiliary_zero - value_and_grad[1] / value_and_grad[0]) /
1339 ratio;
1340 }
1341 else if (index >= 2 && index < degree - 1)
1342 {
1343 this->lagrange_support_points[0] = 0.;
1344 this->lagrange_support_points[1] = 0.;
1345 for (unsigned int m = 0, c = 2; m < degree - 3; ++m)
1346 if (m + 2 != index)
1347 this->lagrange_support_points[c++] = jacobi_roots[m];
1348 this->lagrange_support_points[degree - 2] = 1.;
1349 this->lagrange_support_points[degree - 1] = 1.;
1350
1351 // ensure that the polynomial evaluates to one at the respective
1352 // nodal point
1353 this->lagrange_weight = 1. / this->value(jacobi_roots[index - 2]);
1354 }
1355 else if (index == degree - 1)
1356 {
1357 this->lagrange_support_points[0] = 0.;
1358 this->lagrange_support_points[1] = 0.;
1359 for (unsigned int m = 0; m < degree - 3; ++m)
1360 this->lagrange_support_points[m + 2] = jacobi_roots[m];
1361 this->lagrange_support_points[degree - 1] = 1.;
1362
1363 std::vector<Point<1>> points(degree);
1364 double ratio = 1.;
1365 for (unsigned int i = 0; i < degree; ++i)
1366 {
1367 points[i][0] = this->lagrange_support_points[i];
1368 if (i < degree - 1)
1369 ratio *= 1. - this->lagrange_support_points[i];
1370 }
1371 Polynomial<double> helper(points, degree - 1);
1372 std::vector<double> value_and_grad(2);
1373 helper.value(1., value_and_grad);
1374 Assert(std::abs(value_and_grad[0]) > 1e-10,
1375 ExcInternalError("There should not be a zero at x=1."));
1376
1377 const double auxiliary_zero =
1378 find_support_point_x_star(jacobi_roots);
1379 this->lagrange_weight =
1380 (-1. / auxiliary_zero - value_and_grad[1] / value_and_grad[0]) /
1381 ratio;
1382 }
1383 else if (index == degree)
1384 {
1385 const double auxiliary_zero =
1386 find_support_point_x_star(jacobi_roots);
1387 this->lagrange_support_points[0] = 0.;
1388 this->lagrange_support_points[1] = 0.;
1389 for (unsigned int m = 0; m < degree - 3; ++m)
1390 this->lagrange_support_points[m + 2] = jacobi_roots[m];
1391 this->lagrange_support_points[degree - 1] = 1. - auxiliary_zero;
1392
1393 // ensure that the polynomial evaluates to one at x=1
1394 this->lagrange_weight = 1. / this->value(1.);
1395 }
1396 }
1397 }
1398
1399
1400
1401 std::vector<Polynomial<double>>
1403 {
1404 std::vector<Polynomial<double>> basis(degree + 1);
1405
1406 for (unsigned int i = 0; i <= degree; ++i)
1407 basis[i] = HermiteLikeInterpolation(degree, i);
1408
1409 return basis;
1410 }
1411
1412} // namespace Polynomials
1413
1414// ------------------ explicit instantiations --------------- //
1415
1416#ifndef DOXYGEN
1417namespace Polynomials
1418{
1419 template class Polynomial<float>;
1420 template class Polynomial<double>;
1421 template class Polynomial<long double>;
1422
1423 template void
1424 Polynomial<float>::shift(const float offset);
1425 template void
1426 Polynomial<float>::shift(const double offset);
1427 template void
1428 Polynomial<double>::shift(const double offset);
1429 template void
1430 Polynomial<long double>::shift(const long double offset);
1431 template void
1432 Polynomial<float>::shift(const long double offset);
1433 template void
1434 Polynomial<double>::shift(const long double offset);
1435
1436 template class Monomial<float>;
1437 template class Monomial<double>;
1438 template class Monomial<long double>;
1439} // namespace Polynomials
1440#endif // DOXYGEN
1441
Definition point.h:111
HermiteInterpolation(const unsigned int p)
static std::vector< Polynomial< double > > generate_complete_basis(const unsigned int p)
static std::vector< Polynomial< double > > generate_complete_basis(const unsigned int degree)
HermiteLikeInterpolation(const unsigned int degree, const unsigned int index)
static std::vector< std::unique_ptr< const std::vector< double > > > recursive_coefficients
Definition polynomial.h:599
Hierarchical(const unsigned int p)
static void compute_coefficients(const unsigned int p)
static const std::vector< double > & get_coefficients(const unsigned int p)
static std::vector< Polynomial< double > > generate_complete_basis(const unsigned int degree)
static std::shared_mutex coefficients_lock
Definition polynomial.h:605
static void compute_coefficients(const unsigned int n, const unsigned int support_point, std::vector< double > &a)
LagrangeEquidistant(const unsigned int n, const unsigned int support_point)
static std::vector< Polynomial< double > > generate_complete_basis(const unsigned int degree)
static std::vector< Polynomial< double > > generate_complete_basis(const unsigned int degree)
Legendre(const unsigned int p)
std::vector< double > compute_coefficients(const unsigned int p)
Lobatto(const unsigned int p=0)
static std::vector< Polynomial< double > > generate_complete_basis(const unsigned int p)
static std::vector< Polynomial< number > > generate_complete_basis(const unsigned int degree)
Monomial(const unsigned int n, const double coefficient=1.)
static std::vector< Point< 1 > > create_vector_of_roots(unsigned int n)
number value(const number x) const
Definition polynomial.h:935
bool operator==(const Polynomial< number > &p) const
std::vector< number > coefficients
Definition polynomial.h:323
Polynomial< number > primitive() const
Polynomial< number > & operator+=(const Polynomial< number > &p)
Polynomial< number > derivative() const
void transform_into_standard_form()
Definition polynomial.cc:94
void scale(const number factor)
Polynomial< number > & operator-=(const Polynomial< number > &p)
std::vector< number > lagrange_support_points
Definition polynomial.h:335
void shift(const number2 offset)
void print(std::ostream &out) const
static void multiply(std::vector< number > &coefficients, const number factor)
Polynomial< number > & operator*=(const double s)
virtual std::size_t memory_consumption() const
unsigned int degree() const
Definition polynomial.h:918
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ASSERT_UNREACHABLE()
static ::ExceptionBase & ExcZero()
static ::ExceptionBase & ExcNotImplemented()
static ::ExceptionBase & ExcEmptyObject()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
std::enable_if_t< std::is_fundamental_v< T >, std::size_t > memory_consumption(const T &t)
std::vector< Point< 1 > > generate_equidistant_unit_points(const unsigned int n)
std::vector< Polynomial< double > > generate_complete_Lagrange_basis(const std::vector< Point< 1 > > &points)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)