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