1 //                                               -*- C++ -*-
2 /**
3  *  @brief
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/ExponentialModel.hxx"
22 #include "openturns/SpecFunc.hxx"
23 #include "openturns/PersistentObjectFactory.hxx"
24 #include "openturns/Os.hxx"
25 
26 BEGIN_NAMESPACE_OPENTURNS
27 
28 /**
29  * @class ExponentialModel
30  */
31 
32 CLASSNAMEINIT(ExponentialModel)
33 
34 static const Factory<ExponentialModel> Factory_ExponentialModel;
35 
36 /* Constructor from input dimension */
ExponentialModel(const UnsignedInteger inputDimension)37 ExponentialModel::ExponentialModel(const UnsignedInteger inputDimension)
38   : CovarianceModelImplementation(inputDimension)
39 {
40   isStationary_ = true;
41 }
42 
43 /** Standard constructor with scale and amplitude parameters parameters */
ExponentialModel(const Point & scale,const Point & amplitude)44 ExponentialModel::ExponentialModel(const Point & scale,
45                                    const Point & amplitude)
46   : CovarianceModelImplementation(scale, amplitude)
47 {
48   isStationary_ = true;
49 }
50 
51 /** Standard constructor with scale, amplitude and spatial correlation parameters parameters */
ExponentialModel(const Point & scale,const Point & amplitude,const CorrelationMatrix & spatialCorrelation)52 ExponentialModel::ExponentialModel(const Point & scale,
53                                    const Point & amplitude,
54                                    const CorrelationMatrix & spatialCorrelation)
55   : CovarianceModelImplementation(scale, amplitude, spatialCorrelation)
56 {
57   isStationary_ = true;
58 }
59 
60 /** Standard constructor with scale and spatial covariance parameters parameters */
ExponentialModel(const Point & scale,const CovarianceMatrix & spatialCovariance)61 ExponentialModel::ExponentialModel(const Point & scale,
62                                    const CovarianceMatrix & spatialCovariance)
63   : CovarianceModelImplementation(scale, spatialCovariance)
64 {
65   isStationary_ = true;
66 }
67 
68 /* Virtual constructor */
clone() const69 ExponentialModel * ExponentialModel::clone() const
70 {
71   return new ExponentialModel(*this);
72 }
73 
operator ()(const Point & tau) const74 SquareMatrix ExponentialModel::operator()(const Point &tau) const
75 {
76   // L2 norm of tau / scale
77   Scalar tauOverThetaNorm = 0.0;
78   for (UnsignedInteger i = 0; i < getInputDimension(); ++i)
79   {
80     const Scalar dx = tau[i] / scale_[i];
81     tauOverThetaNorm += dx * dx;
82   }
83   tauOverThetaNorm = sqrt(tauOverThetaNorm);
84   // Return value
85   Scalar factor = 1.0;
86   if (tauOverThetaNorm == 0.0)
87     factor = 1.0 + nuggetFactor_;
88   else
89     factor = exp(-tauOverThetaNorm);
90   SquareMatrix output(outputCovariance_);
91   output.getImplementation()->symmetrize();
92   return output * factor;
93 }
94 
95 /* Computation of the covariance function, stationary interface
96  * C_{i,j}(tau) = amplitude_i * R_{i,j} * amplitude_j  * exp(-|tau / scale|)
97  * C_{i,i}(tau) = amplitude_i^2  * exp(-|tau / scale|)
98  */
computeAsScalar(const Point & tau) const99 Scalar ExponentialModel::computeAsScalar(const Point &tau) const
100 {
101   if (outputDimension_ != 1)
102     throw InvalidArgumentException(HERE) << "Error : ExponentialModel::computeAsScalar(tau) should be only used if output dimension is 1. Here, output dimension = " << outputDimension_;
103   if (tau.getDimension() != inputDimension_)
104     throw InvalidArgumentException(HERE) << "In ExponentialModel::computeStandardRepresentative: expected a shift of dimension=" << getInputDimension() << ", got dimension=" << tau.getDimension();
105 
106   // L2 norm of tau / scale
107   Scalar tauOverThetaNorm = 0.0;
108   for (UnsignedInteger i = 0; i < getInputDimension(); ++i)
109   {
110     const Scalar dx = tau[i] / scale_[i];
111     tauOverThetaNorm += dx * dx;
112   }
113   tauOverThetaNorm = sqrt(tauOverThetaNorm);
114   // Return value
115   return (tauOverThetaNorm == 0.0 ? amplitude_[0] * amplitude_[0] * (1.0 + nuggetFactor_) : amplitude_[0] * amplitude_[0] * exp(-tauOverThetaNorm));
116 }
117 
118 /* Computation of the covariance function, stationary interface
119  * C_{i,j}(tau) = amplitude_i * R_{i,j} * amplitude_j  * exp(-|tau / scale|)
120  * C_{i,i}(tau) = amplitude_i^2  * exp(-|tau / scale|)
121  */
computeAsScalar(const Collection<Scalar>::const_iterator & s_begin,const Collection<Scalar>::const_iterator & t_begin) const122 Scalar ExponentialModel::computeAsScalar(const Collection<Scalar>::const_iterator &s_begin,
123     const Collection<Scalar>::const_iterator &t_begin) const
124 {
125   if (outputDimension_ != 1)
126     throw InvalidArgumentException(HERE) << "Error : ExponentialModel::computeAsScalar(it, it) should be only used if output dimension is 1. Here, output dimension = " << outputDimension_;
127 
128   Scalar tauOverThetaNorm = 0;
129   Collection<Scalar>::const_iterator s_it = s_begin;
130   Collection<Scalar>::const_iterator t_it = t_begin;
131   for (UnsignedInteger i = 0; i < inputDimension_; ++i, ++s_it, ++t_it)
132   {
133     const Scalar dx = (*s_it - *t_it) / scale_[i];
134     tauOverThetaNorm += dx * dx;
135   }
136   tauOverThetaNorm = sqrt(tauOverThetaNorm);
137   return (tauOverThetaNorm == 0.0 ? amplitude_[0] * amplitude_[0] * (1.0 + nuggetFactor_) : amplitude_[0] * amplitude_[0] * exp(-tauOverThetaNorm));
138 }
139 
computeAsScalar(const Scalar tau) const140 Scalar ExponentialModel::computeAsScalar(const Scalar tau) const
141 {
142   if (inputDimension_ != 1)
143     throw NotDefinedException(HERE) << "Error: the covariance model has input dimension=" << inputDimension_ << ", expected input dimension=1.";
144   if (outputDimension_ != 1)
145     throw NotDefinedException(HERE) << "Error: the covariance model has output dimension=" << outputDimension_ << ", expected dimension=1.";
146 
147   const Scalar tauOverThetaNorm = std::abs(tau / scale_[0]);
148   // Return value
149   const CovarianceMatrix & outputCovariance = outputCovariance_;
150   return (tauOverThetaNorm <= SpecFunc::ScalarEpsilon ? outputCovariance(0, 0) * (1.0 + nuggetFactor_) : outputCovariance(0, 0) * exp(-tauOverThetaNorm));
151 }
152 
153 /** Gradient */
partialGradient(const Point & s,const Point & t) const154 Matrix ExponentialModel::partialGradient(const Point & s,
155     const Point & t) const
156 {
157   // Computation of the gradient
158   if (s.getDimension() != getInputDimension()) throw InvalidArgumentException(HERE) << "ExponentialModel::partialGradient, the point s has dimension=" << s.getDimension() << ", expected dimension=" << getInputDimension();
159   if (t.getDimension() != getInputDimension()) throw InvalidArgumentException(HERE) << "ExponentialModel::partialGradient, the point t has dimension=" << t.getDimension() << ", expected dimension=" << getInputDimension();
160 
161   Scalar norm = 0.0;
162   Scalar dx = 0.0;
163   for (UnsignedInteger i = 0; i < getInputDimension(); ++i)
164   {
165     dx = (s[i] - t[i]) / scale_[i];
166     norm += dx * dx;
167   }
168   if (norm == 0)
169     throw InvalidArgumentException(HERE) << "ExponentialModel::partialGradient, the points t and s are equal. Covariance model has no derivate for that case.";
170   norm = std::sqrt(norm);
171   // Covariance matrix write S * rho(tau), so gradient writes Sigma * grad(rho) where * is a 'kroneker',
172   SquareMatrix covariance(outputCovariance_);
173   covariance.getImplementation()->symmetrize();
174   Point covariancePoint(*covariance.getImplementation());
175   // Finally assemble the final matrix
176   const Scalar value = -std::exp(-norm) / norm;
177   Matrix gradient(getInputDimension(), covariancePoint.getDimension());
178   for (UnsignedInteger j = 0; j < covariancePoint.getDimension(); ++ j)
179     for (UnsignedInteger i = 0; i < getInputDimension(); ++i)
180       gradient(i, j) = covariancePoint[j] * (s[i] - t[i]) / (scale_[i] * scale_[i]) * value;
181   return gradient;
182 }
183 
184 /* Discretize the covariance function on a given TimeGrid */
discretize(const RegularGrid & timeGrid) const185 CovarianceMatrix ExponentialModel::discretize(const RegularGrid & timeGrid) const
186 {
187   const UnsignedInteger size = timeGrid.getN();
188   const UnsignedInteger fullSize = size * getOutputDimension();
189   const Scalar timeStep = timeGrid.getStep();
190 
191   CovarianceMatrix cov(fullSize);
192 
193   // The stationary property of this model allows to optimize the discretization
194   // over a regular time grid: the large covariance matrix is block-diagonal
195   // Fill the matrix by block-diagonal
196   // The main diagonal has a specific treatment as only its lower triangular part
197   // has to be copied
198   const SquareMatrix covTau0( operator()( 0.0 ) );
199 
200   // Loop over the main diagonal block
201   for (UnsignedInteger block = 0; block < size; ++block)
202   {
203     const UnsignedInteger base = block * getOutputDimension();
204     // Copy of the lower triangle only
205     for (UnsignedInteger i = 0; i < getOutputDimension(); ++i)
206     {
207       // The diagonal part
208       cov( base + i,
209            base + i ) = covTau0(i, i);
210       // The lower off-diagonal part if needed
211       if (!isDiagonal_)
212         for (UnsignedInteger j = 0; j < i; ++j)
213           cov( base + i,
214                base + j ) = covTau0(i, j);
215     } // Lower triangle
216   } // block
217   // Loop over the remaining diagonal blocks
218   for (UnsignedInteger diag = 1; diag < size; ++diag)
219   {
220     const SquareMatrix covTau( operator()( diag * timeStep ) );
221 
222     // Loop over the main block diagonal
223     for (UnsignedInteger block = 0; block < size - diag; ++block)
224     {
225       const UnsignedInteger base = block * getOutputDimension();
226       const UnsignedInteger baseDiag = (block + diag) * getOutputDimension();
227       // Copy of the full block
228       for (UnsignedInteger i = 0; i < getOutputDimension(); ++i)
229       {
230         // The diagonal part
231         cov(base + i, baseDiag + i) = covTau(i, i);
232         // The off-diagonal part if needed
233         if (!isDiagonal_)
234         {
235           for (UnsignedInteger j = 0; j < i; ++j)
236             cov(base + i, baseDiag + j) = covTau(i, j);
237           for (UnsignedInteger j = i + 1; j < getOutputDimension(); ++j)
238             cov(base + i, baseDiag + j) = covTau(i, j);
239         } // Off-diagonal
240       } // Full block
241     } // block
242   } // Off-diagonal blocks
243 
244   return cov;
245 }
246 
247 /* String converter */
__repr__() const248 String ExponentialModel::__repr__() const
249 {
250   OSS oss(true);
251   oss << "class=" << ExponentialModel::GetClassName();
252   oss << " scale=" << getScale()
253       << " amplitude=" << getAmplitude()
254       << " spatial correlation=" << getOutputCorrelation()
255       << " isDiagonal=" << isDiagonal();
256   return oss;
257 }
258 
259 /* String converter */
__str__(const String & offset) const260 String ExponentialModel::__str__(const String & offset) const
261 {
262   OSS oss(false);
263   oss << ExponentialModel::GetClassName();
264   oss << "(scale=" << getScale()
265       << ", amplitude=" << getAmplitude();
266   if (!isDiagonal_)
267     oss << ", spatial correlation=" << Os::GetEndOfLine() << offset << getOutputCorrelation().__str__(offset);
268   else
269     oss << ", no spatial correlation";
270   oss << ")";
271   return oss;
272 }
273 
save(Advocate & adv) const274 void ExponentialModel::save(Advocate & adv) const
275 {
276   CovarianceModelImplementation::save(adv);
277 }
278 
279 /* Method load() reloads the object from the StorageManager */
load(Advocate & adv)280 void ExponentialModel::load(Advocate & adv)
281 {
282   CovarianceModelImplementation::load(adv);
283 }
284 
285 END_NAMESPACE_OPENTURNS
286