1 //                                               -*- C++ -*-
2 /**
3  *  @brief This is a 1D polynomial
4  *
5  *  Copyright 2005-2021 Airbus-EDF-IMACS-ONERA-Phimeca
6  *
7  *  This library is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with this library.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 #include "openturns/OrthogonalUniVariatePolynomial.hxx"
22 #include "openturns/OSS.hxx"
23 #include "openturns/PersistentObjectFactory.hxx"
24 #include "openturns/SquareMatrix.hxx"
25 #include "openturns/Exception.hxx"
26 #include "openturns/Lapack.hxx"
27 
28 BEGIN_NAMESPACE_OPENTURNS
29 
30 
31 
32 CLASSNAMEINIT(OrthogonalUniVariatePolynomial)
33 
34 static const Factory<OrthogonalUniVariatePolynomial> Factory_OrthogonalUniVariatePolynomial;
35 
36 
37 /* Default constructor */
OrthogonalUniVariatePolynomial()38 OrthogonalUniVariatePolynomial::OrthogonalUniVariatePolynomial()
39   : UniVariatePolynomialImplementation(),
40     recurrenceCoefficients_(0)
41 {
42   coefficients_ = Coefficients(1, 1.0);
43 }
44 
45 
46 /* Constructor from recurrence coefficients */
OrthogonalUniVariatePolynomial(const CoefficientsCollection & recurrenceCoefficients)47 OrthogonalUniVariatePolynomial::OrthogonalUniVariatePolynomial(const CoefficientsCollection & recurrenceCoefficients)
48   : UniVariatePolynomialImplementation(),
49     recurrenceCoefficients_(recurrenceCoefficients)
50 {
51   // Build the coefficients using the recurrence coefficients
52   coefficients_ = Coefficients(buildCoefficients(recurrenceCoefficients.getSize()));
53 }
54 
55 
56 /* Constructor from recurrence coefficients and coefficients */
OrthogonalUniVariatePolynomial(const CoefficientsCollection & recurrenceCoefficients,const Coefficients & coefficients)57 OrthogonalUniVariatePolynomial::OrthogonalUniVariatePolynomial(const CoefficientsCollection & recurrenceCoefficients,
58     const Coefficients & coefficients)
59   : UniVariatePolynomialImplementation(),
60     recurrenceCoefficients_(recurrenceCoefficients)
61 {
62   // Set the value of the coefficients, stored in the upper class
63   coefficients_ = coefficients;
64 }
65 
66 
67 /* Build the coefficients of the polynomial based on the recurrence coefficients */
buildCoefficients(const UnsignedInteger n)68 OrthogonalUniVariatePolynomial::Coefficients OrthogonalUniVariatePolynomial::buildCoefficients(const UnsignedInteger n)
69 {
70   // Constant polynomial equals to 1
71   if (n == 0) return Coefficients(1, 1.0);
72   // Other cases
73   Coefficients coefficientsN(n + 1);
74   Coefficients coefficientsNMinus1(buildCoefficients(n - 1));
75   // Leading term
76   const Coefficients aN(recurrenceCoefficients_[n - 1]);
77   coefficientsN[n] = aN[0] * coefficientsNMinus1[n - 1];
78   // Constant term, case n = 1
79   coefficientsN[0] = aN[1] * coefficientsNMinus1[0];
80   if (n == 1) return coefficientsN;
81   // Constant term, case n >= 2
82   Coefficients coefficientsNMinus2(buildCoefficients(n - 2));
83   coefficientsN[0] += aN[2] * coefficientsNMinus2[0];
84   // Leading term
85   coefficientsN[n] = aN[0] * coefficientsNMinus1[n - 1];
86   // Second leading term
87   coefficientsN[n - 1] = aN[0] * coefficientsNMinus1[n - 2] + aN[1] * coefficientsNMinus1[n - 1];
88   // Constant term
89   coefficientsN[0] = aN[1] * coefficientsNMinus1[0] + aN[2] * coefficientsNMinus2[0];
90   // Remaining terms
91   for (UnsignedInteger i = 1; i < n - 1; ++i)
92     coefficientsN[i] = aN[0] * coefficientsNMinus1[i - 1] + aN[1] * coefficientsNMinus1[i] + aN[2] * coefficientsNMinus2[i];
93   return coefficientsN;
94 }
95 
96 
97 /* Virtual constructor */
clone() const98 OrthogonalUniVariatePolynomial * OrthogonalUniVariatePolynomial::clone() const
99 {
100   return new OrthogonalUniVariatePolynomial(*this);
101 }
102 
103 
104 /* OrthogonalUniVariatePolynomial are evaluated as functors */
operator ()(const Scalar x) const105 Scalar OrthogonalUniVariatePolynomial::operator() (const Scalar x) const
106 {
107   const UnsignedInteger size = recurrenceCoefficients_.getSize();
108   Scalar uN = 1.0;
109   // Special case: degree == 0, constant unitary polynomial
110   if (size == 0) return uN;
111   Scalar aN2 = recurrenceCoefficients_[size - 1][2];
112   Scalar aN1 = recurrenceCoefficients_[size - 1][1];
113   Scalar aN0 = recurrenceCoefficients_[size - 1][0];
114   Scalar uNMinus1 = aN0 * x + aN1;
115   // Special case: degree == 1, affine polynomial
116   if (size == 1) return uNMinus1;
117   Scalar uNMinus2 = 0.0;
118   // General case, use Clenshaw's algorithm for a stable evaluation of the polynomial
119   // The summation must be done in reverse order to get the best stability
120   // The three terms recurrence relation is:
121   // Pn+1(x) = (a0[n] * x + a1[n]) * Pn(x) + a2[n] * Pn-1(x)
122   // with P-1 = 0, P0 = 1
123   for (UnsignedInteger n = size - 1; n > 0; --n)
124   {
125     const Scalar aNMinus12 = recurrenceCoefficients_[n - 1][2];
126     const Scalar aNMinus11 = recurrenceCoefficients_[n - 1][1];
127     const Scalar aNMinus10 = recurrenceCoefficients_[n - 1][0];
128     uNMinus2 = (aNMinus10 * x + aNMinus11) * uNMinus1 + aN2 * uN;
129     uN = uNMinus1;
130     uNMinus1 = uNMinus2;
131     aN2 = aNMinus12;
132     aN1 = aNMinus11;
133     aN0 = aNMinus10;
134   }
135   return uNMinus2;
136 }
137 
138 
getRecurrenceCoefficients() const139 OrthogonalUniVariatePolynomial::CoefficientsCollection OrthogonalUniVariatePolynomial::getRecurrenceCoefficients() const
140 {
141   return recurrenceCoefficients_;
142 }
143 
144 
145 /* Roots of the polynomial of degree n as the eigenvalues of the associated Jacobi matrix */
146 /* Jn = [alpha_0 sqrt(beta_1) 0 ...
147    sqrt(beta_1) alpha_1 sqrt(beta_2) 0 ...
148    0 sqrt(beta_2) alpha_2 sqrt(beta_3) 0 ...
149    |
150    0 ... 0 sqrt(beta_{n-1}) alpha_{n-1}] */
getRoots() const151 OrthogonalUniVariatePolynomial::ComplexCollection OrthogonalUniVariatePolynomial::getRoots() const
152 {
153   const UnsignedInteger n = getDegree();
154   if (n == 0) throw InvalidArgumentException(HERE) << "Error: cannot compute the roots of a constant polynomial.";
155   // gauss integration rule
156   char jobz('N');
157   int ljobz(1);
158   Point d(n);
159   Point e(n - 1);
160   Coefficients recurrenceCoefficientsI(recurrenceCoefficients_[0]);
161   Scalar alphaPrec = recurrenceCoefficientsI[0];
162   d[0] = -recurrenceCoefficientsI[1] / alphaPrec;
163   for (UnsignedInteger i = 1; i < n; ++i)
164   {
165     recurrenceCoefficientsI = recurrenceCoefficients_[i];
166     d[i]     = -recurrenceCoefficientsI[1] / recurrenceCoefficientsI[0];
167     e[i - 1] = sqrt(-recurrenceCoefficientsI[2] / (recurrenceCoefficientsI[0] * alphaPrec));
168     alphaPrec = recurrenceCoefficientsI[0];
169   }
170   int ldz(n);
171   SquareMatrix z(n);
172   Point work(2 * n - 2);
173   int info;
174   dstev_(&jobz, &ldz, &d[0], &e[0], &z(0, 0), &ldz, &work[0], &info, &ljobz);
175   if (info != 0) throw InternalException(HERE) << "Lapack DSTEV: error code=" << info;
176   ComplexCollection result(n);
177   for (UnsignedInteger i = 0; i < n; ++i) result[i] = Complex(d[i], 0.0);
178   return result;
179 }
180 
181 /* Method save() stores the object through the StorageManager */
save(Advocate & adv) const182 void OrthogonalUniVariatePolynomial::save(Advocate & adv) const
183 {
184   UniVariatePolynomialImplementation::save(adv);
185   adv.saveAttribute( "recurrenceCoefficients_", recurrenceCoefficients_ );
186 }
187 
188 /* Method load() reloads the object from the StorageManager */
load(Advocate & adv)189 void OrthogonalUniVariatePolynomial::load(Advocate & adv)
190 {
191   UniVariatePolynomialImplementation::load(adv);
192   adv.loadAttribute( "recurrenceCoefficients_", recurrenceCoefficients_ );
193 }
194 
195 
196 END_NAMESPACE_OPENTURNS
197