1 // -*- C++ -*-
2 /**
3 * @brief Basis selection algorithm
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 <limits>
23 #include "openturns/Log.hxx"
24 #include "openturns/Indices.hxx"
25 #include "openturns/PersistentObjectFactory.hxx"
26 #include "openturns/PenalizedLeastSquaresAlgorithm.hxx"
27 #include "openturns/LeastSquaresMetaModelSelection.hxx"
28 #include "openturns/SVDMethod.hxx"
29 #include "openturns/CholeskyMethod.hxx"
30 #include "openturns/QRMethod.hxx"
31 #include "openturns/SpecFunc.hxx"
32
33 BEGIN_NAMESPACE_OPENTURNS
34
35 CLASSNAMEINIT(LeastSquaresMetaModelSelection)
36
37 static const Factory<LeastSquaresMetaModelSelection> Factory_LeastSquaresMetaModelSelection;
38
39 /* Default constructor */
LeastSquaresMetaModelSelection()40 LeastSquaresMetaModelSelection::LeastSquaresMetaModelSelection()
41 : ApproximationAlgorithmImplementation()
42 {
43 // Nothing to do
44 }
45
46 /* Default constructor */
LeastSquaresMetaModelSelection(const Sample & x,const Sample & y,const FunctionCollection & psi,const Indices & indices,const BasisSequenceFactory & basisSequenceFactory,const FittingAlgorithm & fittingAlgorithm)47 LeastSquaresMetaModelSelection::LeastSquaresMetaModelSelection(const Sample & x,
48 const Sample & y,
49 const FunctionCollection & psi,
50 const Indices & indices,
51 const BasisSequenceFactory & basisSequenceFactory,
52 const FittingAlgorithm & fittingAlgorithm)
53 : ApproximationAlgorithmImplementation( x, y, psi, indices )
54 , basisSequenceFactory_(basisSequenceFactory)
55 , fittingAlgorithm_(fittingAlgorithm)
56
57 {
58 // Nothing to do
59 }
60
61 /* Default constructor */
LeastSquaresMetaModelSelection(const Sample & x,const Sample & y,const Point & weight,const FunctionCollection & psi,const Indices & indices,const BasisSequenceFactory & basisSequenceFactory,const FittingAlgorithm & fittingAlgorithm)62 LeastSquaresMetaModelSelection::LeastSquaresMetaModelSelection(const Sample & x,
63 const Sample & y,
64 const Point & weight,
65 const FunctionCollection & psi,
66 const Indices & indices,
67 const BasisSequenceFactory & basisSequenceFactory,
68 const FittingAlgorithm & fittingAlgorithm)
69 : ApproximationAlgorithmImplementation( x, y, weight, psi, indices )
70 , basisSequenceFactory_(basisSequenceFactory)
71 , fittingAlgorithm_(fittingAlgorithm)
72 {
73 // Nothing to do
74 }
75
76
77 /* Virtual constructor */
clone() const78 LeastSquaresMetaModelSelection * LeastSquaresMetaModelSelection::clone() const
79 {
80 return new LeastSquaresMetaModelSelection( *this );
81 }
82
83 /* BasisSequenceFactory accessor */
setBasisSequenceFactory(const BasisSequenceFactory & basisSequenceFactory)84 void LeastSquaresMetaModelSelection::setBasisSequenceFactory(const BasisSequenceFactory & basisSequenceFactory)
85 {
86 basisSequenceFactory_ = basisSequenceFactory;
87 }
88
getBasisSequenceFactory() const89 BasisSequenceFactory LeastSquaresMetaModelSelection::getBasisSequenceFactory() const
90 {
91 return basisSequenceFactory_;
92 }
93
94 /* FittingAlgorithm accessor */
setFittingAlgorithm(const FittingAlgorithm & fittingAlgorithm)95 void LeastSquaresMetaModelSelection::setFittingAlgorithm(const FittingAlgorithm & fittingAlgorithm)
96 {
97 fittingAlgorithm_ = fittingAlgorithm;
98 }
99
getFittingAlgorithm() const100 FittingAlgorithm LeastSquaresMetaModelSelection::getFittingAlgorithm() const
101 {
102 return fittingAlgorithm_;
103 }
104
105
106 /* String converter */
__repr__() const107 String LeastSquaresMetaModelSelection::__repr__() const
108 {
109 return OSS() << "class=" << getClassName()
110 << " basisSequenceFactory=" << basisSequenceFactory_
111 << " fittingAlgorithm=" << fittingAlgorithm_;
112 }
113
114 /* Perform the selection */
run(const DesignProxy & proxy)115 void LeastSquaresMetaModelSelection::run(const DesignProxy & proxy)
116 {
117 // for each sub-basis ...
118 Scalar minimumError = SpecFunc::MaxScalar;
119
120 const String methodName(ResourceMap::GetAsString("LeastSquaresMetaModelSelection-DecompositionMethod"));
121 LeastSquaresMethod method(LeastSquaresMethod::Build(methodName, proxy, weight_, currentIndices_));
122
123 Indices optimalBasisIndices;
124 UnsignedInteger iterations = 0;
125
126 basisSequenceFactory_.initialize();
127 basisSequenceFactory_.updateBasis(method, y_);
128
129 // for each sub-basis ...
130 const Scalar alpha = std::max(1.0, ResourceMap::GetAsScalar("LeastSquaresMetaModelSelection-MaximumErrorFactor"));
131 const Scalar errorThreshold = std::max(0.0, ResourceMap::GetAsScalar("LeastSquaresMetaModelSelection-ErrorThreshold"));
132 const Scalar maximumError = std::max(0.0, ResourceMap::GetAsScalar("LeastSquaresMetaModelSelection-MaximumError"));
133 while ((basisSequenceFactory_.getImplementation()->addedPsi_k_ranks_.getSize() > 0) || (basisSequenceFactory_.getImplementation()->removedPsi_k_ranks_.getSize() > 0))
134 {
135 // retrieve the i-th basis of the sequence
136 const Scalar error = fittingAlgorithm_.run(method, y_);
137 LOGINFO(OSS() << "\nsubbasis=" << iterations << ", size=" << basisSequenceFactory_.getImplementation()->currentIndices_.getSize() << ", error=" << error << ", qSquare=" << 1.0 - error);
138
139 if (error < minimumError)
140 {
141 optimalBasisIndices = basisSequenceFactory_.getImplementation()->currentIndices_;
142 minimumError = error;
143 }
144 else
145 {
146 if (!(error <= alpha * minimumError))
147 {
148 LOGINFO(OSS() << "Error=" << error << " larger than " << alpha << "*" << minimumError << "=" << alpha * minimumError);
149 break;
150 }
151 if (error > maximumError)
152 {
153 LOGINFO(OSS() << "Error=" << error << " larger than " << maximumError);
154 break;
155 }
156 }
157 if (!(minimumError >= errorThreshold))
158 {
159 LOGINFO(OSS() << "Minimum error=" << minimumError << " smaller than threshold=" << errorThreshold);
160 break;
161 }
162 basisSequenceFactory_.updateBasis(method, y_);
163
164 ++ iterations;
165 }
166
167 // recompute the coefficients of the selected sparse metamodel by least-square regression
168 PenalizedLeastSquaresAlgorithm penalizedLeastSquaresAlgorithm(x_, y_, weight_, method.getBasis(), optimalBasisIndices);
169 penalizedLeastSquaresAlgorithm.run(proxy);
170 const Point optimalBasisCoefficients(penalizedLeastSquaresAlgorithm.getCoefficients());
171 const Scalar optimalResidual = penalizedLeastSquaresAlgorithm.getResidual();
172 // New relative error based on cross-validation error
173 const Scalar optimalRelativeError = minimumError / y_.getSize();
174
175 // compute the coefficients in the master basis from the ones in the optimal sub-basis
176 Point optimalCoefficients( currentIndices_.getSize() );
177 for (UnsignedInteger i = 0; i < optimalBasisIndices.getSize(); ++ i)
178 {
179 for (UnsignedInteger j = 0; j < currentIndices_.getSize(); ++ j)
180 if (currentIndices_[j] == optimalBasisIndices[i])
181 optimalCoefficients[j] = optimalBasisCoefficients[i];
182 }
183
184 setCoefficients(optimalCoefficients);
185 setResidual(optimalResidual);
186 setRelativeError(optimalRelativeError);
187
188 LOGINFO(OSS() << "optimalBasisIndices=" << optimalBasisIndices);
189 LOGINFO(OSS() << "optimalError=" << minimumError);
190 LOGINFO(OSS() << "optimalResidual=" << optimalResidual);
191 LOGINFO(OSS() << "optimalRelativeError=" << optimalRelativeError);
192 }
193
194
195 /* Method save() stores the object through the StorageManager */
save(Advocate & adv) const196 void LeastSquaresMetaModelSelection::save(Advocate & adv) const
197 {
198 ApproximationAlgorithmImplementation::save(adv);
199 adv.saveAttribute( "basisSequenceFactory_", basisSequenceFactory_ );
200 adv.saveAttribute( "fittingAlgorithm_", fittingAlgorithm_ );
201 }
202
203
204 /* Method load() reloads the object from the StorageManager */
load(Advocate & adv)205 void LeastSquaresMetaModelSelection::load(Advocate & adv)
206 {
207 ApproximationAlgorithmImplementation::load(adv);
208 adv.loadAttribute( "basisSequenceFactory_", basisSequenceFactory_ );
209 adv.loadAttribute( "fittingAlgorithm_", fittingAlgorithm_ );
210 }
211
212
213 END_NAMESPACE_OPENTURNS
214