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