1 // -*- C++ -*-
2 /**
3 * @brief The MaximumDistribution distribution
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 <cstdlib>
22 #include <cmath>
23
24 #include "openturns/MaximumDistribution.hxx"
25 #include "openturns/PersistentObjectFactory.hxx"
26 #include "openturns/ComposedDistribution.hxx"
27
28 BEGIN_NAMESPACE_OPENTURNS
29
30 CLASSNAMEINIT(MaximumDistribution)
31
32 static const Factory<MaximumDistribution> Factory_MaximumDistribution;
33
34 /* Default constructor */
MaximumDistribution()35 MaximumDistribution::MaximumDistribution()
36 : DistributionImplementation()
37 , distribution_()
38 , allSame_(true)
39 , variablesNumber_(1)
40 {
41 setName("MaximumDistribution");
42 setDimension(1);
43 // Adjust the truncation interval and the distribution range
44 setDistribution(Distribution());
45 }
46
47 /* Parameters constructor */
MaximumDistribution(const Distribution & distribution)48 MaximumDistribution::MaximumDistribution(const Distribution & distribution)
49 : DistributionImplementation()
50 , distribution_()
51 , allSame_(false)
52 , variablesNumber_(distribution.getDimension())
53 {
54 setName("MaximumDistribution");
55 setDimension(1);
56 setDistribution(distribution);
57 }
58
59 /* Parameters constructor */
MaximumDistribution(const DistributionCollection & collection)60 MaximumDistribution::MaximumDistribution(const DistributionCollection & collection)
61 : DistributionImplementation()
62 , distribution_()
63 , allSame_(true)
64 , variablesNumber_(collection.getSize())
65 {
66 if (variablesNumber_ == 0) throw InvalidArgumentException(HERE) << "Error: cannot take the maximum of an empty collection of distributions";
67 for (UnsignedInteger i = 0; i < variablesNumber_; ++i)
68 if (collection[i].getDimension() != 1) throw InvalidArgumentException(HERE) << "Error: cannot take the maximum of a collection of multivariate distributions, here distribution=" << i << " has dimension=" << collection[i].getDimension();
69 setName("MaximumDistribution");
70 setDimension(1);
71 for (UnsignedInteger i = 0; i < variablesNumber_; ++i)
72 if (collection[i] != collection[0])
73 {
74 allSame_ = false;
75 break;
76 }
77 if (allSame_) setDistribution(collection[0]);
78 else setDistribution(ComposedDistribution(collection));
79 }
80
81 /* Parameters constructor */
MaximumDistribution(const Distribution & distribution,const UnsignedInteger variablesNumber)82 MaximumDistribution::MaximumDistribution(const Distribution & distribution,
83 const UnsignedInteger variablesNumber)
84 : DistributionImplementation()
85 , distribution_()
86 , allSame_(true)
87 , variablesNumber_(variablesNumber)
88 {
89 if (variablesNumber_ == 0) throw InvalidArgumentException(HERE) << "Error: cannot take the maximum of an empty collection of distributions";
90 if (distribution.getDimension() != 1) throw InvalidArgumentException(HERE) << "Error: cannot take the maximum of a collection of multivariate distributions, here distribution=0 has dimension=" << distribution.getDimension();
91 setName("MaximumDistribution");
92 setDimension(1);
93 setDistribution(distribution);
94 }
95
96 /* Comparison operator */
operator ==(const MaximumDistribution & other) const97 Bool MaximumDistribution::operator ==(const MaximumDistribution & other) const
98 {
99 if (this == &other) return true;
100 return (distribution_ == other.distribution_);
101 }
102
equals(const DistributionImplementation & other) const103 Bool MaximumDistribution::equals(const DistributionImplementation & other) const
104 {
105 const MaximumDistribution* p_other = dynamic_cast<const MaximumDistribution*>(&other);
106 return p_other && (*this == *p_other);
107 }
108
109 /* String converter */
__repr__() const110 String MaximumDistribution::__repr__() const
111 {
112 OSS oss;
113 oss << "class=" << MaximumDistribution::GetClassName()
114 << " name=" << getName()
115 << " distribution=" << distribution_;
116 return oss;
117 }
118
__str__(const String &) const119 String MaximumDistribution::__str__(const String & ) const
120 {
121 OSS oss;
122 oss << getClassName() << "(" << getDistribution().__str__() << ")";
123 return oss;
124 }
125
126 /* Virtual constructor */
clone() const127 MaximumDistribution * MaximumDistribution::clone() const
128 {
129 return new MaximumDistribution(*this);
130 }
131
132 /* Compute the numerical range of the distribution given the parameters values */
computeRange()133 void MaximumDistribution::computeRange()
134 {
135 if (allSame_) setRange(distribution_.getRange());
136 const Point lower(distribution_.getRange().getLowerBound());
137 const Point upper(distribution_.getRange().getUpperBound());
138 setRange(Interval(*std::max_element(lower.begin(), lower.end()), *std::max_element(upper.begin(), upper.end())));
139 }
140
141 /* Get one realization of the distribution */
getRealization() const142 Point MaximumDistribution::getRealization() const
143 {
144 if (allSame_) return distribution_.getSample(variablesNumber_).getMax();
145 const Point realization(distribution_.getRealization());
146 return Point(1, *std::max_element(realization.begin(), realization.end()));
147 }
148
149 /* Get the PDF of the distribution */
computePDF(const Point & point) const150 Scalar MaximumDistribution::computePDF(const Point & point) const
151 {
152 if (point.getDimension() != 1) throw InvalidArgumentException(HERE) << "Error: the given point must have dimension=1, here dimension=" << point.getDimension();
153 if ((point[0] <= getRange().getLowerBound()[0]) || (point[0] >= getRange().getUpperBound()[0])) return 0.0;
154 // Special case for identical independent variables
155 if (allSame_) return variablesNumber_ * distribution_.computePDF(point) * std::pow(distribution_.computeCDF(point), static_cast<Scalar>(variablesNumber_) - 1.0);
156 // General case
157 if (!distribution_.hasIndependentCopula()) DistributionImplementation::computePDF(point);
158 // Special treatment of the independent copula case
159 const UnsignedInteger size = distribution_.getDimension();
160 Point marginalCDF(size);
161 Scalar product = 1.0;
162 DistributionCollection marginals(size);
163 for (UnsignedInteger i = 0; i < size; ++i)
164 {
165 marginals[i] = distribution_.getMarginal(i);
166 const Scalar cdf = marginals[i].computeCDF(point);
167 if (cdf == 0) return 0.0;
168 marginalCDF[i] = cdf;
169 product *= cdf;
170 }
171 Scalar sum = 0.0;
172 for (UnsignedInteger i = 0; i < size; ++i)
173 sum += marginals[i].computePDF(point) / marginalCDF[i];
174 return sum * product;
175 }
176
177 /* Get the CDF of the distribution */
computeCDF(const Point & point) const178 Scalar MaximumDistribution::computeCDF(const Point & point) const
179 {
180 if (point.getDimension() != 1) throw InvalidArgumentException(HERE) << "Error: the given point must have dimension=1, here dimension=" << point.getDimension();
181 // Special case for identical independent variables
182 if (allSame_) return std::pow(distribution_.computeCDF(point), static_cast<Scalar>(variablesNumber_));
183 // General case
184 return distribution_.computeCDF(Point(distribution_.getDimension(), point[0]));
185 }
186
187 /* Parameters value and description accessor */
getParametersCollection() const188 MaximumDistribution::PointWithDescriptionCollection MaximumDistribution::getParametersCollection() const
189 {
190 // This is done on purpose to distinguish the case allSame == True
191 if (allSame_) return getDistribution().getParametersCollection();
192 return distribution_.getParametersCollection();
193 }
194
setParametersCollection(const PointCollection & parametersCollection)195 void MaximumDistribution::setParametersCollection(const PointCollection & parametersCollection)
196 {
197 // This trick is needed n order to cope with the case allSame == True
198 if (allSame_)
199 {
200 Distribution clone(getDistribution());
201 clone.setParametersCollection(parametersCollection);
202 distribution_ = clone;
203 }
204 else
205 distribution_.setParametersCollection(parametersCollection);
206 }
207
208 /* Distribution accessor */
setDistribution(const Distribution & distribution)209 void MaximumDistribution::setDistribution(const Distribution & distribution)
210 {
211 // Here we suppose that variablesNumber_ has already been initialized with the
212 // correct value, ie either the distribution dimension is equal to 1 and
213 // variablesNumber_ can take any positive value, or the distribution dimension
214 // is greater than 1 and variablesNumber_ is equalt to this dimension
215 const UnsignedInteger dimension = distribution.getDimension();
216 if ((dimension > 1) && (dimension != variablesNumber_)) throw InvalidArgumentException(HERE) << "Error: the distribution dimension=" << dimension << " does not match the number of variables=" << variablesNumber_;
217 distribution_ = distribution;
218 allSame_ = (dimension == 1);
219 isAlreadyComputedMean_ = false;
220 isAlreadyComputedCovariance_ = false;
221 isAlreadyCreatedGeneratingFunction_ = false;
222 isParallel_ = distribution_.getImplementation()->isParallel();
223 computeRange();
224 }
225
getDistribution() const226 Distribution MaximumDistribution::getDistribution() const
227 {
228 if (allSame_) return ComposedDistribution(DistributionCollection(variablesNumber_, distribution_));
229 return distribution_;
230 }
231
isContinuous() const232 Bool MaximumDistribution::isContinuous() const
233 {
234 return distribution_.isContinuous();
235 }
236
237 /* Tell if the distribution is discrete */
isDiscrete() const238 Bool MaximumDistribution::isDiscrete() const
239 {
240 return distribution_.isDiscrete();
241 }
242
243 /* Tell if the distribution is integer valued */
isIntegral() const244 Bool MaximumDistribution::isIntegral() const
245 {
246 return distribution_.isIntegral();
247 }
248
249 /* Method save() stores the object through the StorageManager */
save(Advocate & adv) const250 void MaximumDistribution::save(Advocate & adv) const
251 {
252 DistributionImplementation::save(adv);
253 adv.saveAttribute( "distribution_", distribution_ );
254 adv.saveAttribute( "allSame_", allSame_ );
255 adv.saveAttribute( "variablesNumber_", variablesNumber_ );
256 }
257
258 /* Method load() reloads the object from the StorageManager */
load(Advocate & adv)259 void MaximumDistribution::load(Advocate & adv)
260 {
261 DistributionImplementation::load(adv);
262 Distribution distribution;
263 adv.loadAttribute( "distribution_", distribution );
264 adv.loadAttribute( "allSame_", allSame_ );
265 adv.loadAttribute( "variablesNumber_", variablesNumber_ );
266 setDistribution(distribution);
267 }
268
269 END_NAMESPACE_OPENTURNS
270