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