1 //                                               -*- C++ -*-
2 /**
3  * @brief This class binds a Python object to an OpenTURNS' RandomVector
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 "openturns/PythonRandomVector.hxx"
23 #include "openturns/OSS.hxx"
24 #include "openturns/Description.hxx"
25 #include "openturns/PythonWrappingFunctions.hxx"
26 #include "openturns/PersistentObjectFactory.hxx"
27 #include "openturns/Exception.hxx"
28 
29 BEGIN_NAMESPACE_OPENTURNS
30 
31 
32 CLASSNAMEINIT(PythonRandomVector)
33 
34 static const Factory<PythonRandomVector> Factory_PythonRandomVector;
35 
36 
37 
38 /* Default constructor */
PythonRandomVector()39 PythonRandomVector::PythonRandomVector()
40   : RandomVectorImplementation(),
41     pyObj_(0)
42 {
43   // Nothing to do
44 }
45 
46 
47 /* Constructor from Python object*/
PythonRandomVector(PyObject * pyObject)48 PythonRandomVector::PythonRandomVector(PyObject * pyObject)
49   : RandomVectorImplementation(),
50     pyObj_(pyObject)
51 {
52   if ( !PyObject_HasAttrString( pyObj_, const_cast<char *>("getRealization") ) ) throw InvalidArgumentException(HERE) << "Error: the given object does not have a getRealization() method.";
53 
54   Py_XINCREF( pyObj_ );
55 
56   // Set the name of the object as its Python classname
57   ScopedPyObjectPointer cls(PyObject_GetAttrString ( pyObj_,
58                             const_cast<char *>( "__class__" ) ));
59   ScopedPyObjectPointer name(PyObject_GetAttrString( cls.get(),
60                              const_cast<char *>( "__name__" ) ));
61 
62   setName( checkAndConvert< _PyString_, String >(name.get()) );
63 
64   const UnsignedInteger dimension  = getDimension();
65   Description description(dimension);
66   ScopedPyObjectPointer desc(PyObject_CallMethod ( pyObj_,
67                              const_cast<char *>( "getDescription" ),
68                              const_cast<char *>( "()" ) ));
69   if ( ( desc.get() != NULL )
70        && PySequence_Check( desc.get() )
71        && ( PySequence_Size( desc.get() ) == static_cast<SignedInteger>(dimension) ) )
72   {
73     description = convert< _PySequence_, Description >( desc.get() );
74   }
75   else for (UnsignedInteger i = 0; i < dimension; ++i) description[i] = (OSS() << "x" << i);
76   setDescription(description);
77 }
78 
79 /* Virtual constructor */
clone() const80 PythonRandomVector * PythonRandomVector::clone() const
81 {
82   return new PythonRandomVector(*this);
83 }
84 
85 /* Copy constructor */
PythonRandomVector(const PythonRandomVector & other)86 PythonRandomVector::PythonRandomVector(const PythonRandomVector & other)
87   : RandomVectorImplementation(other),
88     pyObj_()
89 {
90   ScopedPyObjectPointer pyObjClone(deepCopy(other.pyObj_));
91   pyObj_ = pyObjClone.get();
92   Py_XINCREF(pyObj_);
93 }
94 
95 /* Copy assignment operator */
operator =(const PythonRandomVector & rhs)96 PythonRandomVector & PythonRandomVector::operator=(const PythonRandomVector & rhs)
97 {
98   if (this != &rhs)
99   {
100     RandomVectorImplementation::operator=(rhs);
101     ScopedPyObjectPointer pyObjClone(deepCopy(rhs.pyObj_));
102     pyObj_ = pyObjClone.get();
103     Py_XINCREF(pyObj_);
104   }
105   return *this;
106 }
107 
108 /* Destructor */
~PythonRandomVector()109 PythonRandomVector::~PythonRandomVector()
110 {
111   Py_XDECREF(pyObj_);
112 }
113 
114 /* Comparison operator */
operator ==(const PythonRandomVector &) const115 Bool PythonRandomVector::operator ==(const PythonRandomVector & ) const
116 {
117   return true;
118 }
119 
120 /* String converter */
__repr__() const121 String PythonRandomVector::__repr__() const
122 {
123   OSS oss;
124   oss << "class=" << PythonRandomVector::GetClassName()
125       << " name=" << getName()
126       << " description=" << getDescription();
127   return oss;
128 }
129 
130 /* String converter */
__str__(const String &) const131 String PythonRandomVector::__str__(const String & ) const
132 {
133   OSS oss;
134   oss << "class=" << PythonRandomVector::GetClassName()
135       << " name=" << getName();
136   return oss;
137 }
138 
139 
140 /* Here is the interface that all derived class must implement */
141 
142 
143 /* Accessor for input point dimension */
getDimension() const144 UnsignedInteger PythonRandomVector::getDimension() const
145 {
146   ScopedPyObjectPointer result(PyObject_CallMethod ( pyObj_,
147                                const_cast<char *>( "getDimension" ),
148                                const_cast<char *>( "()" ) ));
149   if ( result.isNull() )
150   {
151     handleException();
152   }
153 
154   UnsignedInteger dim = convert< _PyInt_, UnsignedInteger >( result.get() );
155   return dim;
156 }
157 
getRealization() const158 Point PythonRandomVector::getRealization() const
159 {
160   ScopedPyObjectPointer result(PyObject_CallMethod ( pyObj_,
161                                const_cast<char *>( "getRealization" ),
162                                const_cast<char *>( "()" ) ));
163   if ( result.isNull() )
164   {
165     handleException();
166   }
167   Point point(convert<_PySequence_, Point>(result.get()));
168   return point;
169 }
170 
171 
172 /* Numerical sample accessor */
getSample(const UnsignedInteger size) const173 Sample PythonRandomVector::getSample(const UnsignedInteger size) const
174 {
175   Sample sample;
176 
177   if ( PyObject_HasAttrString( pyObj_, const_cast<char *>("getSample") ) )
178   {
179     ScopedPyObjectPointer methodName(convert< String, _PyString_>( "getSample" ));
180     ScopedPyObjectPointer sizeArg(convert< UnsignedInteger, _PyInt_ >(size));
181     ScopedPyObjectPointer result(PyObject_CallMethodObjArgs( pyObj_,
182                                  methodName.get(),
183                                  sizeArg.get(), NULL ));
184     if ( result.get() )
185     {
186       sample = convert<_PySequence_, Sample>(result.get());
187       if (sample.getSize() != size) throw InvalidDimensionException(HERE) << "Sample returned by PythonRandomVector has incorrect size. Got " << sample.getSize() << ". Expected" << size;
188     }
189   }
190   else
191   {
192     sample = RandomVectorImplementation::getSample(size);
193   }
194   return sample;
195 }
196 
197 
198 /* Mean accessor */
getMean() const199 Point PythonRandomVector::getMean() const
200 {
201   ScopedPyObjectPointer result(PyObject_CallMethod ( pyObj_,
202                                const_cast<char *>( "getMean" ),
203                                const_cast<char *>( "()" ) ));
204   if ( result.isNull() )
205   {
206     handleException();
207   }
208 
209   Point mean(convert<_PySequence_, Point>(result.get()));
210   if (mean.getDimension() != getDimension()) throw InvalidDimensionException(HERE) << "Mean returned by PythonRandomVector has incorrect dimension. Got " << mean.getDimension() << ". Expected" << getDimension();
211   return mean;
212 }
213 
214 /* Covariance accessor */
getCovariance() const215 CovarianceMatrix PythonRandomVector::getCovariance() const
216 {
217   ScopedPyObjectPointer result(PyObject_CallMethod ( pyObj_,
218                                const_cast<char *>( "getCovariance" ),
219                                const_cast<char *>( "()" ) ));
220   if ( result.isNull() )
221   {
222     handleException();
223   }
224 
225   CovarianceMatrix covariance(convert<_PySequence_, CovarianceMatrix>(result.get()));
226   if (covariance.getDimension() != getDimension()) throw InvalidDimensionException(HERE) << "Covariance matrix returned by PythonRandomVector has incorrect dimension. Got " << covariance.getDimension() << ". Expected" << getDimension();
227 
228   return covariance;
229 }
230 
isEvent() const231 Bool PythonRandomVector::isEvent() const
232 {
233   if (PyObject_HasAttrString(pyObj_, const_cast<char *>("isEvent")))
234   {
235     ScopedPyObjectPointer result(PyObject_CallMethod ( pyObj_,
236                                  const_cast<char *>( "isEvent" ),
237                                  const_cast<char *>( "()" ) ));
238     if ( result.isNull() )
239     {
240       handleException();
241     }
242 
243     Bool isEvent = checkAndConvert<_PyBool_, Bool>(result.get());
244     return isEvent;
245   }
246   else
247   {
248     return RandomVectorImplementation::isEvent();
249   }
250 }
251 
252 /* Parameter accessor */
getParameter() const253 Point PythonRandomVector::getParameter() const
254 {
255   if (PyObject_HasAttrString(pyObj_, const_cast<char *>("getParameter")))
256   {
257     ScopedPyObjectPointer callResult(PyObject_CallMethod( pyObj_,
258                                      const_cast<char *>( "getParameter" ),
259                                      const_cast<char *>( "()" ) ));
260     if (callResult.isNull())
261     {
262       handleException();
263     }
264     Point parameter(convert< _PySequence_, Point >(callResult.get()));
265     return parameter;
266   }
267   else
268   {
269     // RandomVectorImplementation::getParameter throws
270     return Point();
271   }
272 }
273 
274 /* Parameter accessor */
setParameter(const Point & parameter)275 void PythonRandomVector::setParameter(const Point & parameter)
276 {
277   if (PyObject_HasAttrString(pyObj_, const_cast<char *>("setParameter")))
278   {
279     ScopedPyObjectPointer methodName(convert< String, _PyString_ >("setParameter"));
280     ScopedPyObjectPointer parameterArg(convert< Point, _PySequence_ >(parameter));
281     ScopedPyObjectPointer callResult(PyObject_CallMethodObjArgs( pyObj_,
282                                      methodName.get(),
283                                      parameterArg.get(), NULL));
284     if (callResult.isNull())
285     {
286       handleException();
287     }
288   }
289 }
290 
291 /* Parameter description accessor */
getParameterDescription() const292 Description PythonRandomVector::getParameterDescription() const
293 {
294   if (PyObject_HasAttrString(pyObj_, const_cast<char *>("getParameterDescription")))
295   {
296     ScopedPyObjectPointer callResult(PyObject_CallMethod( pyObj_,
297                                      const_cast<char *>( "getParameterDescription" ),
298                                      const_cast<char *>( "()" ) ));
299     if (callResult.isNull())
300     {
301       handleException();
302     }
303     Description parameterDescription(convert< _PySequence_, Description >(callResult.get()));
304     return parameterDescription;
305   }
306   else
307   {
308     // RandomVectorImplementation::getParameterDescription throws
309     return Description();
310   }
311 }
312 
313 /* Method save() stores the object through the StorageManager */
save(Advocate & adv) const314 void PythonRandomVector::save(Advocate & adv) const
315 {
316   RandomVectorImplementation::save( adv );
317 
318   pickleSave(adv, pyObj_);
319 }
320 
321 
322 /* Method save() reloads the object from the StorageManager */
load(Advocate & adv)323 void PythonRandomVector::load(Advocate & adv)
324 {
325   RandomVectorImplementation::load( adv );
326 
327   pickleLoad(adv, pyObj_);
328 }
329 
330 
331 END_NAMESPACE_OPENTURNS
332