1 // -*- C++ -*-
2 /**
3 * @brief Karhunen-Loeve decomposition and by-products
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
22 #include "openturns/PersistentObjectFactory.hxx"
23 #include "openturns/KarhunenLoeveResultImplementation.hxx"
24 #include "openturns/LinearFunction.hxx"
25 #include "openturns/Cloud.hxx"
26 #include "openturns/IdentityMatrix.hxx"
27 #include "openturns/ComposedFunction.hxx"
28 #include "openturns/LinearCombinationFunction.hxx"
29 #include "openturns/P1LagrangeEvaluation.hxx"
30
31 BEGIN_NAMESPACE_OPENTURNS
32
33 CLASSNAMEINIT(KarhunenLoeveResultImplementation)
34
35 static const Factory<KarhunenLoeveResultImplementation> Factory_KarhunenLoeveResultImplementation;
36
37 /* Default constructor */
KarhunenLoeveResultImplementation()38 KarhunenLoeveResultImplementation::KarhunenLoeveResultImplementation()
39 : PersistentObject()
40 , covariance_()
41 , threshold_(0.0)
42 , eigenvalues_()
43 , modes_()
44 , modesAsProcessSample_()
45 , projection_()
46 {
47 // Nothing to do
48 }
49
50 /* Default constructor */
KarhunenLoeveResultImplementation(const CovarianceModel & covariance,const Scalar threshold,const Point & eigenvalues,const FunctionCollection & modes,const ProcessSample & modesAsProcessSample,const Matrix & projection)51 KarhunenLoeveResultImplementation::KarhunenLoeveResultImplementation(const CovarianceModel & covariance,
52 const Scalar threshold,
53 const Point & eigenvalues,
54 const FunctionCollection & modes,
55 const ProcessSample & modesAsProcessSample,
56 const Matrix & projection)
57 : PersistentObject()
58 , covariance_(covariance)
59 , threshold_(threshold)
60 , eigenvalues_(eigenvalues)
61 , modes_(modes)
62 , modesAsProcessSample_(modesAsProcessSample)
63 , projection_(projection)
64 {
65 // Nothing to do
66 }
67
68 /* Virtual constructor */
clone() const69 KarhunenLoeveResultImplementation * KarhunenLoeveResultImplementation::clone() const
70 {
71 return new KarhunenLoeveResultImplementation(*this);
72 }
73
74 /* Threshold accessor */
getThreshold() const75 Scalar KarhunenLoeveResultImplementation::getThreshold() const
76 {
77 return threshold_;
78 }
79
80 /* Covariance model accessor */
getCovarianceModel() const81 CovarianceModel KarhunenLoeveResultImplementation::getCovarianceModel() const
82 {
83 return covariance_;
84 }
85
86 /* Eigenvalues accessor */
getEigenvalues() const87 Point KarhunenLoeveResultImplementation::getEigenvalues() const
88 {
89 return eigenvalues_;
90 }
91
drawEigenvalues() const92 Graph KarhunenLoeveResultImplementation::drawEigenvalues() const
93 {
94 Graph graph("Karhunen-Loeve eigenvalues", "Index", "Eigenvalue", true, "topright");
95 const UnsignedInteger K = eigenvalues_.getSize();
96 Point indices(K);
97 for (UnsignedInteger i = 0; i < K; ++ i)
98 indices[i] = i;
99 Cloud cloud(indices, eigenvalues_);
100 graph.add(cloud);
101 graph.setGrid(true);
102 return graph;
103 }
104
drawCumulatedEigenvaluesRemainder() const105 Graph KarhunenLoeveResultImplementation::drawCumulatedEigenvaluesRemainder() const
106 {
107 Graph graph("Karhunen-Loeve eigenvalues", "Index", "Cumulated eigenvalue normalized remainder", true, "topright");
108 const UnsignedInteger K = eigenvalues_.getSize();
109 Point indices(K);
110 for (UnsignedInteger i = 0; i < K; ++ i)
111 indices[i] = i;
112 Point eigenCumSum(eigenvalues_);
113 for (UnsignedInteger i = 1; i < K; ++ i)
114 eigenCumSum[i] += eigenCumSum[i - 1];
115 if (K > 1)
116 {
117 eigenCumSum /= eigenCumSum[K - 1];
118 eigenCumSum[K - 1] = eigenCumSum[K - 2];// avoids log(0) in log scale
119 }
120 Cloud cloud(indices, Point(K, 1) - eigenCumSum);
121 graph.add(cloud);
122 graph.setGrid(true);
123 return graph;
124 }
125
126 /* Modes accessors */
getModes() const127 KarhunenLoeveResultImplementation::FunctionCollection KarhunenLoeveResultImplementation::getModes() const
128 {
129 return modes_;
130 }
131
getModesAsProcessSample() const132 ProcessSample KarhunenLoeveResultImplementation::getModesAsProcessSample() const
133 {
134 return modesAsProcessSample_;
135 }
136
137 /* Mesh accessor */
getMesh() const138 Mesh KarhunenLoeveResultImplementation::getMesh() const
139 {
140 return modesAsProcessSample_.getMesh();
141 }
142
143 /* Scaled modes accessors */
getScaledModes() const144 KarhunenLoeveResultImplementation::FunctionCollection KarhunenLoeveResultImplementation::getScaledModes() const
145 {
146 Collection<Function> scaledModes(modes_.getSize());
147 if (modes_.getSize() == 0) return scaledModes;
148 const UnsignedInteger dimension = modes_[0].getOutputDimension();
149 const Point zero(dimension);
150 const IdentityMatrix id(dimension);
151 for (UnsignedInteger i = 0; i < scaledModes.getSize(); ++i)
152 {
153 const Function modeI(modes_[i]);
154 LinearFunction scaling(zero, zero, id * std::sqrt(eigenvalues_[i]));
155 scaledModes[i] = ComposedFunction(scaling, modeI);
156 }
157 return scaledModes;
158 }
159
getScaledModesAsProcessSample() const160 ProcessSample KarhunenLoeveResultImplementation::getScaledModesAsProcessSample() const
161 {
162 ProcessSample scaledModesAsProcessSample(modesAsProcessSample_.getMesh(), modesAsProcessSample_.getSize(), modesAsProcessSample_.getDimension());
163 for (UnsignedInteger i = 0; i < modesAsProcessSample_.getSize(); ++i)
164 scaledModesAsProcessSample[i] = modesAsProcessSample_[i] * std::sqrt(eigenvalues_[i]);
165 return scaledModesAsProcessSample;
166 }
167
168 /* Projection matrix accessor */
getProjectionMatrix() const169 Matrix KarhunenLoeveResultImplementation::getProjectionMatrix() const
170 {
171 return projection_;
172 }
173
174 /* Projection method */
project(const FunctionCollection & functionCollection) const175 Sample KarhunenLoeveResultImplementation::project(const FunctionCollection & functionCollection) const
176 {
177 const UnsignedInteger size = functionCollection.getSize();
178 const Sample vertices(modesAsProcessSample_.getMesh().getVertices());
179 Sample functionValues(size, projection_.getNbColumns());
180 for(UnsignedInteger i = 0; i < size; ++i)
181 functionValues[i] = (functionCollection[i](vertices)).getImplementation()->getData();
182 return projection_.getImplementation()->genSampleProd(functionValues, true, false, 'R');
183 }
184
project(const Function & function) const185 Point KarhunenLoeveResultImplementation::project(const Function & function) const
186 {
187 // Evaluate the function over the vertices of the mesh and cast it into a Point
188 const Point functionValues(function(modesAsProcessSample_.getMesh().getVertices()).getImplementation()->getData());
189 return projection_ * functionValues;
190 }
191
project(const Sample & values) const192 Point KarhunenLoeveResultImplementation::project(const Sample & values) const
193 {
194 if (values.getDimension() != modesAsProcessSample_.getDimension())
195 throw InvalidDimensionException(HERE) << "Expected values of dimension " << modesAsProcessSample_.getDimension() << " got " << values.getDimension();
196 return projection_ * values.getImplementation()->getData();
197 }
198
project(const ProcessSample & sample) const199 Sample KarhunenLoeveResultImplementation::project(const ProcessSample & sample) const
200 {
201 if (sample.getDimension() != modesAsProcessSample_.getDimension())
202 throw InvalidDimensionException(HERE) << "Expected values of dimension " << modesAsProcessSample_.getDimension() << " got " << sample.getDimension();
203 const UnsignedInteger size = sample.getSize();
204 if (!(size != 0)) return Sample();
205 const Mesh mesh(modesAsProcessSample_.getMesh());
206 const UnsignedInteger dimension = sample.getDimension();
207 const UnsignedInteger length = mesh.getVerticesNumber();
208 if (sample.getMesh() == mesh)
209 {
210 // result[i] = projection_ * sample.data_[i] if sample.data_ is viewed as a
211 // Sample(size, length * dimension) and result[i] as a Point of dimension rows(projection_)
212 // This can be rewritten with matrix multiplication:
213 // transposed(result) = transposed(sample.data_) * transposed(projection_)
214 // As result and sample.data_ are stored as Sample and not Matrix, we have
215 // result = sample.data_ * transposed(projection_)
216 Sample values(size, length * dimension);
217 for(UnsignedInteger i = 0; i < size; ++i)
218 std::copy(&sample[i](0, 0), &sample[i](0, 0) + length * dimension, &values(i, 0));
219 return projection_.getImplementation()->genSampleProd(values, true, false, 'R');
220 }
221 else
222 {
223 // We build a P1LagrangeEvaluation as if ProcessSample was an aggregated Field.
224 P1LagrangeEvaluation evaluation(sample);
225 // values is Sample(length, size * dimension)
226 const Sample values(evaluation(mesh.getVertices()));
227 // Dispatch values so that it can be multiplied by projection_ like above
228 Sample dispatched(size, length * dimension);
229 for(UnsignedInteger i = 0; i < size; ++i)
230 for(UnsignedInteger j = 0; j < length; ++j)
231 std::copy(&values(j, i * dimension), &values(j, i * dimension) + dimension, &dispatched(i, j * dimension));
232 return projection_.getImplementation()->genSampleProd(dispatched, true, false, 'R');
233 }
234 }
235
236 /* Lift method */
lift(const Point & coefficients) const237 Function KarhunenLoeveResultImplementation::lift(const Point & coefficients) const
238 {
239 const UnsignedInteger dimension = eigenvalues_.getDimension();
240 if (coefficients.getDimension() != dimension)
241 throw InvalidDimensionException(HERE) << "Expected coefficients of dimension " << dimension << " got " << coefficients.getDimension();
242 Point scaledCoefficients(dimension);
243 Collection<Function> functions(dimension);
244 for (UnsignedInteger i = 0; i < dimension; ++i)
245 {
246 scaledCoefficients[i] = std::sqrt(eigenvalues_[i]) * coefficients[i];
247 functions[i] = modes_[i];
248 }
249 return LinearCombinationFunction(functions, scaledCoefficients);
250 }
251
liftAsSample(const Point & coefficients) const252 Sample KarhunenLoeveResultImplementation::liftAsSample(const Point & coefficients) const
253 {
254 const UnsignedInteger dimension = eigenvalues_.getDimension();
255 if (coefficients.getDimension() != dimension)
256 throw InvalidDimensionException(HERE) << "Expected coefficients of dimension " << dimension << " got " << coefficients.getDimension();
257 Sample values(modesAsProcessSample_.getMesh().getVerticesNumber(), modesAsProcessSample_.getDimension());
258 for (UnsignedInteger i = 0; i < dimension; ++i)
259 values += modesAsProcessSample_[i] * (std::sqrt(eigenvalues_[i]) * coefficients[i]);
260 return values;
261 }
262
liftAsField(const Point & coefficients) const263 Field KarhunenLoeveResultImplementation::liftAsField(const Point & coefficients) const
264 {
265 return Field(modesAsProcessSample_.getMesh(), liftAsSample(coefficients));
266 }
267
268 /* String converter */
__repr__() const269 String KarhunenLoeveResultImplementation::__repr__() const
270 {
271 return OSS(true) << "class=" << getClassName()
272 << " covariance model=" << covariance_
273 << " threshold=" << threshold_
274 << " eigenvalues=" << eigenvalues_
275 << " modes=" << modes_
276 << " modesAsProcessSample=" << modesAsProcessSample_
277 << " projection=" << projection_;
278 }
279
__str__(const String &) const280 String KarhunenLoeveResultImplementation::__str__(const String & ) const
281 {
282 return __repr__();
283 }
284
285 /* Method save() stores the object through the StorageManager */
save(Advocate & adv) const286 void KarhunenLoeveResultImplementation::save(Advocate & adv) const
287 {
288 PersistentObject::save(adv);
289 adv.saveAttribute("covariance_", covariance_);
290 adv.saveAttribute("threshold_", threshold_);
291 adv.saveAttribute("eigenvalues_", eigenvalues_);
292 adv.saveAttribute("modes_", modes_);
293 adv.saveAttribute("modesAsProcessSample_", modesAsProcessSample_);
294 adv.saveAttribute("projection_", projection_);
295 }
296
297
298 /* Method load() reloads the object from the StorageManager */
load(Advocate & adv)299 void KarhunenLoeveResultImplementation::load(Advocate & adv)
300 {
301 PersistentObject::load(adv);
302 adv.loadAttribute("covariance_", covariance_);
303 adv.loadAttribute("threshold_", threshold_);
304 adv.loadAttribute("eigenvalues_", eigenvalues_);
305 adv.loadAttribute("modes_", modes_);
306 adv.loadAttribute("modesAsProcessSample_", modesAsProcessSample_);
307 adv.loadAttribute("projection_", projection_);
308 }
309
310
311 END_NAMESPACE_OPENTURNS
312
313