1 #-*-coding:iso-8859-1-*-
3 # Copyright (C) 2008-2014 EDF R&D
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License.
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 # Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D
24 Définit les outils généraux élémentaires.
26 Ce module est destiné à etre appelée par AssimilationStudy pour constituer
27 les objets élémentaires de l'algorithme.
29 __author__ = "Jean-Philippe ARGAUD"
36 # ==============================================================================
39 Classe générale de gestion d'un cache de calculs
42 toleranceInRedundancy = 1.e-18,
43 lenghtOfRedundancy = -1,
46 Les caractéristiques de tolérance peuvent être modifées à la création.
48 self.__tolerBP = float(toleranceInRedundancy)
49 self.__lenghtOR = int(lenghtOfRedundancy)
53 self.__listOPCV = [] # Operator Previous Calculated Points, Results, Point Norms
54 # logging.debug("CM Tolerance de determination des doublons : %.2e"%self.__tolerBP)
56 def wasCalculatedIn(self, xValue ): #, info="" ):
59 for i in xrange(min(len(self.__listOPCV),self.__lenghtOR)-1,-1,-1):
60 if xValue.size != self.__listOPCV[i][0].size:
61 # logging.debug("CM Différence de la taille %s de X et de celle %s du point %i déjà calculé"%(xValue.shape,i,self.__listOPCP[i].shape))
63 if numpy.linalg.norm(numpy.ravel(xValue) - self.__listOPCV[i][0]) < self.__tolerBP * self.__listOPCV[i][2]:
65 __HxV = self.__listOPCV[i][1]
66 # logging.debug("CM Cas%s déja calculé, portant le numéro %i"%(info,i))
70 def storeValueInX(self, xValue, HxValue ):
71 if self.__lenghtOR < 0: self.__lenghtOR = 2 * xValue.size + 2
72 while len(self.__listOPCV) > self.__lenghtOR:
73 # logging.debug("CM Réduction de la liste des cas à %i éléments par suppression du premier"%self.__lenghtOR)
74 self.__listOPCV.pop(0)
75 self.__listOPCV.append( (
76 copy.copy(numpy.ravel(xValue)),
78 numpy.linalg.norm(xValue),
81 # ==============================================================================
84 Classe générale d'interface de type opérateur
91 def __init__(self, fromMethod=None, fromMatrix=None, avoidingRedundancy = True):
93 On construit un objet de ce type en fournissant à l'aide de l'un des
94 deux mots-clé, soit une fonction python, soit matrice.
96 - fromMethod : argument de type fonction Python
97 - fromMatrix : argument adapté au constructeur numpy.matrix
99 self.__NbCallsAsMatrix, self.__NbCallsAsMethod, self.__NbCallsOfCached = 0, 0, 0
100 self.__AvoidRC = bool( avoidingRedundancy )
101 if fromMethod is not None:
102 self.__Method = fromMethod
104 self.__Type = "Method"
105 elif fromMatrix is not None:
107 self.__Matrix = numpy.matrix( fromMatrix, numpy.float )
108 self.__Type = "Matrix"
117 def appliedTo(self, xValue):
119 Permet de restituer le résultat de l'application de l'opérateur à un
120 argument xValue. Cette méthode se contente d'appliquer, son argument
121 devant a priori être du bon type.
123 - xValue : argument adapté pour appliquer l'opérateur
126 __alreadyCalculated, __HxV = Operator.CM.wasCalculatedIn(xValue)
128 __alreadyCalculated = False
130 if __alreadyCalculated:
131 self.__addOneCacheCall()
134 if self.__Matrix is not None:
135 self.__addOneMatrixCall()
136 HxValue = self.__Matrix * xValue
138 self.__addOneMethodCall()
139 HxValue = self.__Method( xValue )
141 Operator.CM.storeValueInX(xValue,HxValue)
145 def appliedControledFormTo(self, (xValue, uValue) ):
147 Permet de restituer le résultat de l'application de l'opérateur à une
148 paire (xValue, uValue). Cette méthode se contente d'appliquer, son
149 argument devant a priori être du bon type. Si la uValue est None,
150 on suppose que l'opérateur ne s'applique qu'à xValue.
152 - xValue : argument X adapté pour appliquer l'opérateur
153 - uValue : argument U adapté pour appliquer l'opérateur
155 if self.__Matrix is not None:
156 self.__addOneMatrixCall()
157 return self.__Matrix * xValue
158 elif uValue is not None:
159 self.__addOneMethodCall()
160 return self.__Method( (xValue, uValue) )
162 self.__addOneMethodCall()
163 return self.__Method( xValue )
165 def appliedInXTo(self, (xNominal, xValue) ):
167 Permet de restituer le résultat de l'application de l'opérateur à un
168 argument xValue, sachant que l'opérateur est valable en xNominal.
169 Cette méthode se contente d'appliquer, son argument devant a priori
170 être du bon type. Si l'opérateur est linéaire car c'est une matrice,
171 alors il est valable en tout point nominal et il n'est pas nécessaire
173 Arguments : une liste contenant
174 - xNominal : argument permettant de donner le point où l'opérateur
175 est construit pour etre ensuite appliqué
176 - xValue : argument adapté pour appliquer l'opérateur
178 if self.__Matrix is not None:
179 self.__addOneMatrixCall()
180 return self.__Matrix * xValue
182 self.__addOneMethodCall()
183 return self.__Method( (xNominal, xValue) )
185 def asMatrix(self, ValueForMethodForm = "UnknownVoidValue"):
187 Permet de renvoyer l'opérateur sous la forme d'une matrice
189 if self.__Matrix is not None:
190 self.__addOneMatrixCall()
192 elif ValueForMethodForm is not "UnknownVoidValue": # Ne pas utiliser "None"
193 self.__addOneMethodCall()
194 return numpy.matrix( self.__Method( (ValueForMethodForm, None) ) )
196 raise ValueError("Matrix form of the operator defined as a function/method requires to give an operating point.")
200 Renvoie la taille sous forme numpy si l'opérateur est disponible sous
201 la forme d'une matrice
203 if self.__Matrix is not None:
204 return self.__Matrix.shape
206 raise ValueError("Matrix form of the operator is not available, nor the shape")
208 def nbcalls(self, which=None):
210 Renvoie les nombres d'évaluations de l'opérateur
213 self.__NbCallsAsMatrix+self.__NbCallsAsMethod,
214 self.__NbCallsAsMatrix,
215 self.__NbCallsAsMethod,
216 self.__NbCallsOfCached,
217 Operator.NbCallsAsMatrix+Operator.NbCallsAsMethod,
218 Operator.NbCallsAsMatrix,
219 Operator.NbCallsAsMethod,
220 Operator.NbCallsOfCached,
222 if which is None: return __nbcalls
223 else: return __nbcalls[which]
225 def __addOneMatrixCall(self):
226 self.__NbCallsAsMatrix += 1 # Decompte local
227 Operator.NbCallsAsMatrix += 1 # Decompte global
229 def __addOneMethodCall(self):
230 self.__NbCallsAsMethod += 1 # Decompte local
231 Operator.NbCallsAsMethod += 1 # Decompte global
233 def __addOneCacheCall(self):
234 self.__NbCallsOfCached += 1 # Decompte local
235 Operator.NbCallsOfCached += 1 # Decompte global
237 # ==============================================================================
240 Classe générale d'interface de type algorithme
242 Elle donne un cadre pour l'écriture d'une classe élémentaire d'algorithme
243 d'assimilation, en fournissant un container (dictionnaire) de variables
244 persistantes initialisées, et des méthodes d'accès à ces variables stockées.
246 Une classe élémentaire d'algorithme doit implémenter la méthode "run".
248 def __init__(self, name):
250 L'initialisation présente permet de fabriquer des variables de stockage
251 disponibles de manière générique dans les algorithmes élémentaires. Ces
252 variables de stockage sont ensuite conservées dans un dictionnaire
253 interne à l'objet, mais auquel on accède par la méthode "get".
255 Les variables prévues sont :
256 - CostFunctionJ : fonction-cout globale, somme des deux parties suivantes
257 - CostFunctionJb : partie ébauche ou background de la fonction-cout
258 - CostFunctionJo : partie observations de la fonction-cout
259 - GradientOfCostFunctionJ : gradient de la fonction-cout globale
260 - GradientOfCostFunctionJb : gradient de la partie ébauche de la fonction-cout
261 - GradientOfCostFunctionJo : gradient de la partie observations de la fonction-cout
262 - CurrentState : état courant lors d'itérations
263 - Analysis : l'analyse Xa
264 - ObservedState : l'état observé H(X)
265 - Innovation : l'innovation : d = Y - H(X)
266 - SigmaObs2 : indicateur de correction optimale des erreurs d'observation
267 - SigmaBck2 : indicateur de correction optimale des erreurs d'ébauche
268 - MahalanobisConsistency : indicateur de consistance des covariances
269 - OMA : Observation moins Analysis : Y - Xa
270 - OMB : Observation moins Background : Y - Xb
271 - AMB : Analysis moins Background : Xa - Xb
272 - APosterioriCovariance : matrice A
273 On peut rajouter des variables à stocker dans l'initialisation de
274 l'algorithme élémentaire qui va hériter de cette classe
276 logging.debug("%s Initialisation"%str(name))
277 self._m = PlatformInfo.SystemUsage()
279 self._name = str( name )
280 self._parameters = {}
281 self.__required_parameters = {}
282 self.StoredVariables = {}
284 self.StoredVariables["CostFunctionJ"] = Persistence.OneScalar(name = "CostFunctionJ")
285 self.StoredVariables["CostFunctionJb"] = Persistence.OneScalar(name = "CostFunctionJb")
286 self.StoredVariables["CostFunctionJo"] = Persistence.OneScalar(name = "CostFunctionJo")
287 self.StoredVariables["GradientOfCostFunctionJ"] = Persistence.OneVector(name = "GradientOfCostFunctionJ")
288 self.StoredVariables["GradientOfCostFunctionJb"] = Persistence.OneVector(name = "GradientOfCostFunctionJb")
289 self.StoredVariables["GradientOfCostFunctionJo"] = Persistence.OneVector(name = "GradientOfCostFunctionJo")
290 self.StoredVariables["CurrentState"] = Persistence.OneVector(name = "CurrentState")
291 self.StoredVariables["Analysis"] = Persistence.OneVector(name = "Analysis")
292 self.StoredVariables["ObservedState"] = Persistence.OneVector(name = "ObservedState")
293 self.StoredVariables["Innovation"] = Persistence.OneVector(name = "Innovation")
294 self.StoredVariables["SigmaObs2"] = Persistence.OneScalar(name = "SigmaObs2")
295 self.StoredVariables["SigmaBck2"] = Persistence.OneScalar(name = "SigmaBck2")
296 self.StoredVariables["MahalanobisConsistency"] = Persistence.OneScalar(name = "MahalanobisConsistency")
297 self.StoredVariables["OMA"] = Persistence.OneVector(name = "OMA")
298 self.StoredVariables["OMB"] = Persistence.OneVector(name = "OMB")
299 self.StoredVariables["BMA"] = Persistence.OneVector(name = "BMA")
300 self.StoredVariables["APosterioriCovariance"] = Persistence.OneMatrix(name = "APosterioriCovariance")
301 self.StoredVariables["SimulationQuantiles"] = Persistence.OneMatrix(name = "SimulationQuantiles")
304 logging.debug("%s Lancement"%self._name)
305 logging.debug("%s Taille mémoire utilisée de %.1f Mo"%(self._name, self._m.getUsedMemory("M")))
308 def _post_run(self,_oH=None):
310 logging.debug("%s Nombre d'évaluation(s) de l'opérateur d'observation direct/tangent/adjoint.: %i/%i/%i"%(self._name, _oH["Direct"].nbcalls(0),_oH["Tangent"].nbcalls(0),_oH["Adjoint"].nbcalls(0)))
311 logging.debug("%s Nombre d'appels au cache d'opérateur d'observation direct/tangent/adjoint..: %i/%i/%i"%(self._name, _oH["Direct"].nbcalls(3),_oH["Tangent"].nbcalls(3),_oH["Adjoint"].nbcalls(3)))
312 logging.debug("%s Taille mémoire utilisée de %.1f Mo"%(self._name, self._m.getUsedMemory("M")))
313 logging.debug("%s Terminé"%self._name)
316 def get(self, key=None):
318 Renvoie l'une des variables stockées identifiée par la clé, ou le
319 dictionnaire de l'ensemble des variables disponibles en l'absence de
320 clé. Ce sont directement les variables sous forme objet qui sont
321 renvoyées, donc les méthodes d'accès à l'objet individuel sont celles
322 des classes de persistance.
325 return self.StoredVariables[key]
327 return self.StoredVariables
329 def has_key(self, key=None):
331 Vérifie si l'une des variables stockées est identifiée par la clé.
333 return self.StoredVariables.has_key(key)
337 Renvoie la liste des clés de variables stockées.
339 return self.StoredVariables.keys()
341 def run(self, Xb=None, Y=None, H=None, M=None, R=None, B=None, Q=None, Parameters=None):
343 Doit implémenter l'opération élémentaire de calcul d'assimilation sous
344 sa forme mathématique la plus naturelle possible.
346 raise NotImplementedError("Mathematical assimilation calculation has not been implemented!")
348 def defineRequiredParameter(self, name = None, default = None, typecast = None, message = None, minval = None, maxval = None, listval = None):
350 Permet de définir dans l'algorithme des paramètres requis et leurs
351 caractéristiques par défaut.
354 raise ValueError("A name is mandatory to define a required parameter.")
356 self.__required_parameters[name] = {
358 "typecast" : typecast,
364 logging.debug("%s %s (valeur par défaut = %s)"%(self._name, message, self.setParameterValue(name)))
366 def getRequiredParameters(self, noDetails=True):
368 Renvoie la liste des noms de paramètres requis ou directement le
369 dictionnaire des paramètres requis.
372 ks = self.__required_parameters.keys()
376 return self.__required_parameters
378 def setParameterValue(self, name=None, value=None):
380 Renvoie la valeur d'un paramètre requis de manière contrôlée
382 default = self.__required_parameters[name]["default"]
383 typecast = self.__required_parameters[name]["typecast"]
384 minval = self.__required_parameters[name]["minval"]
385 maxval = self.__required_parameters[name]["maxval"]
386 listval = self.__required_parameters[name]["listval"]
388 if value is None and default is None:
390 elif value is None and default is not None:
391 if typecast is None: __val = default
392 else: __val = typecast( default )
394 if typecast is None: __val = value
395 else: __val = typecast( value )
397 if minval is not None and __val < minval:
398 raise ValueError("The parameter named \"%s\" of value \"%s\" can not be less than %s."%(name, __val, minval))
399 if maxval is not None and __val > maxval:
400 raise ValueError("The parameter named \"%s\" of value \"%s\" can not be greater than %s."%(name, __val, maxval))
401 if listval is not None:
402 if typecast is list or typecast is tuple or type(__val) is list or type(__val) is tuple:
405 raise ValueError("The value \"%s\" of the parameter named \"%s\" is not allowed, it has to be in the list %s."%(v, name, listval))
406 elif __val not in listval:
407 raise ValueError("The value \"%s\" of the parameter named \"%s\" is not allowed, it has to be in the list %s."%( __val, name,listval))
410 def setParameters(self, fromDico={}):
412 Permet de stocker les paramètres reçus dans le dictionnaire interne.
414 self._parameters.update( fromDico )
415 for k in self.__required_parameters.keys():
416 if k in fromDico.keys():
417 self._parameters[k] = self.setParameterValue(k,fromDico[k])
419 self._parameters[k] = self.setParameterValue(k)
420 logging.debug("%s %s : %s"%(self._name, self.__required_parameters[k]["message"], self._parameters[k]))
422 # ==============================================================================
425 Classe générale d'interface de type diagnostic
427 Ce template s'utilise de la manière suivante : il sert de classe "patron" en
428 même temps que l'une des classes de persistance, comme "OneScalar" par
431 Une classe élémentaire de diagnostic doit implémenter ses deux méthodes, la
432 méthode "_formula" pour écrire explicitement et proprement la formule pour
433 l'écriture mathématique du calcul du diagnostic (méthode interne non
434 publique), et "calculate" pour activer la précédente tout en ayant vérifié
435 et préparé les données, et pour stocker les résultats à chaque pas (méthode
436 externe d'activation).
438 def __init__(self, name = "", parameters = {}):
439 self.name = str(name)
440 self.parameters = dict( parameters )
442 def _formula(self, *args):
444 Doit implémenter l'opération élémentaire de diagnostic sous sa forme
445 mathématique la plus naturelle possible.
447 raise NotImplementedError("Diagnostic mathematical formula has not been implemented!")
449 def calculate(self, *args):
451 Active la formule de calcul avec les arguments correctement rangés
453 raise NotImplementedError("Diagnostic activation method has not been implemented!")
455 # ==============================================================================
458 Classe générale d'interface de type covariance
461 name = "GenericCovariance",
463 asEyeByScalar = None,
464 asEyeByVector = None,
467 Permet de définir une covariance :
468 - asCovariance : entrée des données, comme une matrice compatible avec
469 le constructeur de numpy.matrix
470 - asEyeByScalar : entrée des données comme un seul scalaire de variance,
471 multiplicatif d'une matrice de corrélation identité, aucune matrice
472 n'étant donc explicitement à donner
473 - asEyeByVector : entrée des données comme un seul vecteur de variance,
474 à mettre sur la diagonale d'une matrice de corrélation, aucune matrice
475 n'étant donc explicitement à donner
477 self.__name = str(name)
480 self.__is_scalar = False
481 self.__is_vector = False
482 self.__is_matrix = False
483 if asEyeByScalar is not None:
484 self.__is_scalar = True
485 self.__B = numpy.abs( float(asEyeByScalar) )
488 elif asEyeByVector is not None:
489 self.__is_vector = True
490 self.__B = numpy.abs( numpy.array( numpy.ravel( asEyeByVector ), float ) )
491 self.shape = (self.__B.size,self.__B.size)
492 self.size = self.__B.size**2
493 elif asCovariance is not None:
494 self.__is_matrix = True
495 self.__B = numpy.matrix( asCovariance, float )
496 self.shape = self.__B.shape
497 self.size = self.__B.size
500 # raise ValueError("The %s covariance matrix has to be specified either as a matrix, a vector for its diagonal or a scalar multiplying an identity matrix."%self.__name)
504 def __validate(self):
505 if self.ismatrix() and min(self.shape) != max(self.shape):
506 raise ValueError("The given matrix for %s is not a square one, its shape is %s. Please check your matrix input."%(self.__name,self.shape))
507 if self.isscalar() and self.__B <= 0:
508 raise ValueError("The %s covariance matrix is not positive-definite. Please check your scalar input %s."%(self.__name,self.__B_scalar))
509 if self.isvector() and (self.__B <= 0).any():
510 raise ValueError("The %s covariance matrix is not positive-definite. Please check your vector input."%(self.__name,))
511 if self.ismatrix() and logging.getLogger().level < logging.WARNING: # La verification n'a lieu qu'en debug
513 L = numpy.linalg.cholesky( self.__B )
515 raise ValueError("The %s covariance matrix is not symmetric positive-definite. Please check your matrix input."%(self.__name,))
518 return self.__is_scalar
521 return self.__is_vector
524 return self.__is_matrix
528 return Covariance(self.__name+"I", asCovariance = self.__B.I )
529 elif self.isvector():
530 return Covariance(self.__name+"I", asEyeByVector = 1. / self.__B )
531 elif self.isscalar():
532 return Covariance(self.__name+"I", asEyeByScalar = 1. / self.__B )
538 return Covariance(self.__name+"T", asCovariance = self.__B.T )
539 elif self.isvector():
540 return Covariance(self.__name+"T", asEyeByVector = self.__B )
541 elif self.isscalar():
542 return Covariance(self.__name+"T", asEyeByScalar = self.__B )
546 return Covariance(self.__name+"C", asCovariance = numpy.linalg.cholesky(self.__B) )
547 elif self.isvector():
548 return Covariance(self.__name+"C", asEyeByVector = numpy.sqrt( self.__B ) )
549 elif self.isscalar():
550 return Covariance(self.__name+"C", asEyeByScalar = numpy.sqrt( self.__B ) )
554 return Covariance(self.__name+"H", asCovariance = numpy.linalg.cholesky(self.__B).I )
555 elif self.isvector():
556 return Covariance(self.__name+"H", asEyeByVector = 1.0 / numpy.sqrt( self.__B ) )
557 elif self.isscalar():
558 return Covariance(self.__name+"H", asEyeByScalar = 1.0 / numpy.sqrt( self.__B ) )
560 def diag(self, msize=None):
562 return numpy.diag(self.__B)
563 elif self.isvector():
565 elif self.isscalar():
567 raise ValueError("the size of the %s covariance matrix has to be given in case of definition as a scalar over the diagonal."%(self.__name,))
569 return self.__B * numpy.ones(int(msize))
571 def asfullmatrix(self, msize=None):
574 elif self.isvector():
575 return numpy.matrix( numpy.diag(self.__B), float )
576 elif self.isscalar():
578 raise ValueError("the size of the %s covariance matrix has to be given in case of definition as a scalar over the diagonal."%(self.__name,))
580 return numpy.matrix( self.__B * numpy.eye(int(msize)), float )
582 def trace(self, msize=None):
584 return numpy.trace(self.__B)
585 elif self.isvector():
586 return float(numpy.sum(self.__B))
587 elif self.isscalar():
589 raise ValueError("the size of the %s covariance matrix has to be given in case of definition as a scalar over the diagonal."%(self.__name,))
591 return self.__B * int(msize)
594 return repr(self.__B)
599 def __add__(self, other):
601 return self.__B + numpy.asmatrix(other)
602 elif self.isvector() or self.isscalar():
603 _A = numpy.asarray(other)
604 _A.reshape(_A.size)[::_A.shape[1]+1] += self.__B
605 return numpy.asmatrix(_A)
607 def __radd__(self, other):
608 raise NotImplementedError("%s covariance matrix __radd__ method not available for %s type!"%(self.__name,type(other)))
610 def __sub__(self, other):
612 return self.__B - numpy.asmatrix(other)
613 elif self.isvector() or self.isscalar():
614 _A = numpy.asarray(other)
615 _A.reshape(_A.size)[::_A.shape[1]+1] = self.__B - _A.reshape(_A.size)[::_A.shape[1]+1]
616 return numpy.asmatrix(_A)
618 def __rsub__(self, other):
619 raise NotImplementedError("%s covariance matrix __rsub__ method not available for %s type!"%(self.__name,type(other)))
624 def __mul__(self, other):
625 if self.ismatrix() and isinstance(other,numpy.matrix):
626 return self.__B * other
627 elif self.ismatrix() and (isinstance(other,numpy.ndarray) \
628 or isinstance(other,list) \
629 or isinstance(other,tuple)):
630 if numpy.ravel(other).size == self.shape[1]: # Vecteur
631 return self.__B * numpy.asmatrix(numpy.ravel(other)).T
632 elif numpy.asmatrix(other).shape[0] == self.shape[1]: # Matrice
633 return self.__B * numpy.asmatrix(other)
635 raise ValueError("operands could not be broadcast together with shapes %s %s in %s matrix"%(self.shape,numpy.asmatrix(other).shape,self.__name))
636 elif self.isvector() and (isinstance(other,numpy.matrix) \
637 or isinstance(other,numpy.ndarray) \
638 or isinstance(other,list) \
639 or isinstance(other,tuple)):
640 if numpy.ravel(other).size == self.shape[1]: # Vecteur
641 return numpy.asmatrix(self.__B * numpy.ravel(other)).T
642 elif numpy.asmatrix(other).shape[0] == self.shape[1]: # Matrice
643 return numpy.asmatrix((self.__B * (numpy.asarray(other).transpose())).transpose())
645 raise ValueError("operands could not be broadcast together with shapes %s %s in %s matrix"%(self.shape,numpy.ravel(other).shape,self.__name))
646 elif self.isscalar() and isinstance(other,numpy.matrix):
647 return self.__B * other
648 elif self.isscalar() and (isinstance(other,numpy.ndarray) \
649 or isinstance(other,list) \
650 or isinstance(other,tuple)):
651 if len(numpy.asarray(other).shape) == 1 or numpy.asarray(other).shape[1] == 1 or numpy.asarray(other).shape[0] == 1:
652 return self.__B * numpy.asmatrix(numpy.ravel(other)).T
654 return self.__B * numpy.asmatrix(other)
656 raise NotImplementedError("%s covariance matrix __mul__ method not available for %s type!"%(self.__name,type(other)))
658 def __rmul__(self, other):
659 if self.ismatrix() and isinstance(other,numpy.matrix):
660 return other * self.__B
661 elif self.isvector() and isinstance(other,numpy.matrix):
662 if numpy.ravel(other).size == self.shape[0]: # Vecteur
663 return numpy.asmatrix(numpy.ravel(other) * self.__B)
664 elif numpy.asmatrix(other).shape[1] == self.shape[0]: # Matrice
665 return numpy.asmatrix(numpy.array(other) * self.__B)
667 raise ValueError("operands could not be broadcast together with shapes %s %s in %s matrix"%(self.shape,numpy.ravel(other).shape,self.__name))
668 elif self.isscalar() and isinstance(other,numpy.matrix):
669 return other * self.__B
671 raise NotImplementedError("%s covariance matrix __rmul__ method not available for %s type!"%(self.__name,type(other)))
676 # ==============================================================================
677 if __name__ == "__main__":
678 print '\n AUTODIAGNOSTIC \n'