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