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,
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
264 - Innovation : l'innovation : d = Y - H Xb
265 - SigmaObs2 : indicateur de correction optimale des erreurs d'observation
266 - SigmaBck2 : indicateur de correction optimale des erreurs d'ébauche
267 - MahalanobisConsistency : indicateur de consistance des covariances
268 - OMA : Observation moins Analysis : Y - Xa
269 - OMB : Observation moins Background : Y - Xb
270 - AMB : Analysis moins Background : Xa - Xb
271 - APosterioriCovariance : matrice A
272 On peut rajouter des variables à stocker dans l'initialisation de
273 l'algorithme élémentaire qui va hériter de cette classe
275 logging.debug("%s Initialisation"%str(name))
276 self._m = PlatformInfo.SystemUsage()
278 self._name = str( name )
279 self._parameters = {}
280 self.__required_parameters = {}
281 self.StoredVariables = {}
283 self.StoredVariables["CostFunctionJ"] = Persistence.OneScalar(name = "CostFunctionJ")
284 self.StoredVariables["CostFunctionJb"] = Persistence.OneScalar(name = "CostFunctionJb")
285 self.StoredVariables["CostFunctionJo"] = Persistence.OneScalar(name = "CostFunctionJo")
286 self.StoredVariables["GradientOfCostFunctionJ"] = Persistence.OneVector(name = "GradientOfCostFunctionJ")
287 self.StoredVariables["GradientOfCostFunctionJb"] = Persistence.OneVector(name = "GradientOfCostFunctionJb")
288 self.StoredVariables["GradientOfCostFunctionJo"] = Persistence.OneVector(name = "GradientOfCostFunctionJo")
289 self.StoredVariables["CurrentState"] = Persistence.OneVector(name = "CurrentState")
290 self.StoredVariables["Analysis"] = Persistence.OneVector(name = "Analysis")
291 self.StoredVariables["Innovation"] = Persistence.OneVector(name = "Innovation")
292 self.StoredVariables["SigmaObs2"] = Persistence.OneScalar(name = "SigmaObs2")
293 self.StoredVariables["SigmaBck2"] = Persistence.OneScalar(name = "SigmaBck2")
294 self.StoredVariables["MahalanobisConsistency"] = Persistence.OneScalar(name = "MahalanobisConsistency")
295 self.StoredVariables["OMA"] = Persistence.OneVector(name = "OMA")
296 self.StoredVariables["OMB"] = Persistence.OneVector(name = "OMB")
297 self.StoredVariables["BMA"] = Persistence.OneVector(name = "BMA")
298 self.StoredVariables["APosterioriCovariance"] = Persistence.OneMatrix(name = "APosterioriCovariance")
299 self.StoredVariables["SimulationQuantiles"] = Persistence.OneMatrix(name = "SimulationQuantiles")
302 logging.debug("%s Lancement"%self._name)
303 logging.debug("%s Taille mémoire utilisée de %.1f Mo"%(self._name, self._m.getUsedMemory("M")))
306 def _post_run(self,_oH=None):
308 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)))
309 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)))
310 logging.debug("%s Taille mémoire utilisée de %.1f Mo"%(self._name, self._m.getUsedMemory("M")))
311 logging.debug("%s Terminé"%self._name)
314 def get(self, key=None):
316 Renvoie l'une des variables stockées identifiée par la clé, ou le
317 dictionnaire de l'ensemble des variables disponibles en l'absence de
318 clé. Ce sont directement les variables sous forme objet qui sont
319 renvoyées, donc les méthodes d'accès à l'objet individuel sont celles
320 des classes de persistance.
323 return self.StoredVariables[key]
325 return self.StoredVariables
327 def has_key(self, key=None):
329 Vérifie si l'une des variables stockées est identifiée par la clé.
331 return self.StoredVariables.has_key(key)
335 Renvoie la liste des clés de variables stockées.
337 return self.StoredVariables.keys()
339 def run(self, Xb=None, Y=None, H=None, M=None, R=None, B=None, Q=None, Parameters=None):
341 Doit implémenter l'opération élémentaire de calcul d'assimilation sous
342 sa forme mathématique la plus naturelle possible.
344 raise NotImplementedError("Mathematical assimilation calculation has not been implemented!")
346 def defineRequiredParameter(self, name = None, default = None, typecast = None, message = None, minval = None, maxval = None, listval = None):
348 Permet de définir dans l'algorithme des paramètres requis et leurs
349 caractéristiques par défaut.
352 raise ValueError("A name is mandatory to define a required parameter.")
354 self.__required_parameters[name] = {
356 "typecast" : typecast,
362 logging.debug("%s %s (valeur par défaut = %s)"%(self._name, message, self.setParameterValue(name)))
364 def getRequiredParameters(self, noDetails=True):
366 Renvoie la liste des noms de paramètres requis ou directement le
367 dictionnaire des paramètres requis.
370 ks = self.__required_parameters.keys()
374 return self.__required_parameters
376 def setParameterValue(self, name=None, value=None):
378 Renvoie la valeur d'un paramètre requis de manière contrôlée
380 default = self.__required_parameters[name]["default"]
381 typecast = self.__required_parameters[name]["typecast"]
382 minval = self.__required_parameters[name]["minval"]
383 maxval = self.__required_parameters[name]["maxval"]
384 listval = self.__required_parameters[name]["listval"]
386 if value is None and default is None:
388 elif value is None and default is not None:
389 if typecast is None: __val = default
390 else: __val = typecast( default )
392 if typecast is None: __val = value
393 else: __val = typecast( value )
395 if minval is not None and __val < minval:
396 raise ValueError("The parameter named \"%s\" of value \"%s\" can not be less than %s."%(name, __val, minval))
397 if maxval is not None and __val > maxval:
398 raise ValueError("The parameter named \"%s\" of value \"%s\" can not be greater than %s."%(name, __val, maxval))
399 if listval is not None:
400 if typecast is list or typecast is tuple or type(__val) is list or type(__val) is tuple:
403 raise ValueError("The value \"%s\" of the parameter named \"%s\" is not allowed, it has to be in the list %s."%(v, name, listval))
404 elif __val not in listval:
405 raise ValueError("The value \"%s\" of the parameter named \"%s\" is not allowed, it has to be in the list %s."%( __val, name,listval))
408 def setParameters(self, fromDico={}):
410 Permet de stocker les paramètres reçus dans le dictionnaire interne.
412 self._parameters.update( fromDico )
413 for k in self.__required_parameters.keys():
414 if k in fromDico.keys():
415 self._parameters[k] = self.setParameterValue(k,fromDico[k])
417 self._parameters[k] = self.setParameterValue(k)
418 logging.debug("%s %s : %s"%(self._name, self.__required_parameters[k]["message"], self._parameters[k]))
420 # ==============================================================================
423 Classe générale d'interface de type diagnostic
425 Ce template s'utilise de la manière suivante : il sert de classe "patron" en
426 même temps que l'une des classes de persistance, comme "OneScalar" par
429 Une classe élémentaire de diagnostic doit implémenter ses deux méthodes, la
430 méthode "_formula" pour écrire explicitement et proprement la formule pour
431 l'écriture mathématique du calcul du diagnostic (méthode interne non
432 publique), et "calculate" pour activer la précédente tout en ayant vérifié
433 et préparé les données, et pour stocker les résultats à chaque pas (méthode
434 externe d'activation).
436 def __init__(self, name = "", parameters = {}):
437 self.name = str(name)
438 self.parameters = dict( parameters )
440 def _formula(self, *args):
442 Doit implémenter l'opération élémentaire de diagnostic sous sa forme
443 mathématique la plus naturelle possible.
445 raise NotImplementedError("Diagnostic mathematical formula has not been implemented!")
447 def calculate(self, *args):
449 Active la formule de calcul avec les arguments correctement rangés
451 raise NotImplementedError("Diagnostic activation method has not been implemented!")
453 # ==============================================================================
456 Classe générale d'interface de type covariance
459 name = "GenericCovariance",
461 asEyeByScalar = None,
462 asEyeByVector = None,
465 Permet de définir une covariance :
466 - asCovariance : entrée des données, comme une matrice compatible avec
467 le constructeur de numpy.matrix
468 - asEyeByScalar : entrée des données comme un seul scalaire de variance,
469 multiplicatif d'une matrice de corrélation identité, aucune matrice
470 n'étant donc explicitement à donner
471 - asEyeByVector : entrée des données comme un seul vecteur de variance,
472 à mettre sur la diagonale d'une matrice de corrélation, aucune matrice
473 n'étant donc explicitement à donner
475 self.__name = str(name)
478 self.__is_scalar = False
479 self.__is_vector = False
480 self.__is_matrix = False
481 if asEyeByScalar is not None:
482 self.__is_scalar = True
483 self.__B = numpy.abs( float(asEyeByScalar) )
486 elif asEyeByVector is not None:
487 self.__is_vector = True
488 self.__B = numpy.abs( numpy.array( numpy.ravel( asEyeByVector ), float ) )
489 self.shape = (self.__B.size,self.__B.size)
490 self.size = self.__B.size**2
491 elif asCovariance is not None:
492 self.__is_matrix = True
493 self.__B = numpy.matrix( asCovariance, float )
494 self.shape = self.__B.shape
495 self.size = self.__B.size
498 # 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)
502 def __validate(self):
503 if self.ismatrix() and min(self.shape) != max(self.shape):
504 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))
505 if self.isscalar() and self.__B <= 0:
506 raise ValueError("The %s covariance matrix is not positive-definite. Please check your scalar input %s."%(self.__name,self.__B_scalar))
507 if self.isvector() and (self.__B <= 0).any():
508 raise ValueError("The %s covariance matrix is not positive-definite. Please check your vector input."%(self.__name,))
509 if self.ismatrix() and logging.getLogger().level < logging.WARNING: # La verification n'a lieu qu'en debug
511 L = numpy.linalg.cholesky( self.__B )
513 raise ValueError("The %s covariance matrix is not symmetric positive-definite. Please check your matrix input."%(self.__name,))
516 return self.__is_scalar
519 return self.__is_vector
522 return self.__is_matrix
526 return Covariance(self.__name+"I", asCovariance = self.__B.I )
527 elif self.isvector():
528 return Covariance(self.__name+"I", asEyeByVector = 1. / self.__B )
529 elif self.isscalar():
530 return Covariance(self.__name+"I", asEyeByScalar = 1. / self.__B )
536 return Covariance(self.__name+"T", asCovariance = self.__B.T )
537 elif self.isvector():
538 return Covariance(self.__name+"T", asEyeByVector = self.__B )
539 elif self.isscalar():
540 return Covariance(self.__name+"T", asEyeByScalar = self.__B )
544 return Covariance(self.__name+"C", asCovariance = numpy.linalg.cholesky(self.__B) )
545 elif self.isvector():
546 return Covariance(self.__name+"C", asEyeByVector = numpy.sqrt( self.__B ) )
547 elif self.isscalar():
548 return Covariance(self.__name+"C", asEyeByScalar = numpy.sqrt( self.__B ) )
552 return Covariance(self.__name+"H", asCovariance = numpy.linalg.cholesky(self.__B).I )
553 elif self.isvector():
554 return Covariance(self.__name+"H", asEyeByVector = 1.0 / numpy.sqrt( self.__B ) )
555 elif self.isscalar():
556 return Covariance(self.__name+"H", asEyeByScalar = 1.0 / numpy.sqrt( self.__B ) )
558 def diag(self, msize=None):
560 return numpy.diag(self.__B)
561 elif self.isvector():
563 elif self.isscalar():
565 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,))
567 return self.__B * numpy.ones(int(msize))
569 def asfullmatrix(self, msize=None):
572 elif self.isvector():
573 return numpy.matrix( numpy.diag(self.__B), float )
574 elif self.isscalar():
576 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,))
578 return numpy.matrix( self.__B * numpy.eye(int(msize)), float )
580 def trace(self, msize=None):
582 return numpy.trace(self.__B)
583 elif self.isvector():
584 return float(numpy.sum(self.__B))
585 elif self.isscalar():
587 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,))
589 return self.__B * int(msize)
592 return repr(self.__B)
597 def __add__(self, other):
599 return self.__B + numpy.asmatrix(other)
600 elif self.isvector() or self.isscalar():
601 _A = numpy.asarray(other)
602 _A.reshape(_A.size)[::_A.shape[1]+1] += self.__B
603 return numpy.asmatrix(_A)
605 def __radd__(self, other):
606 raise NotImplementedError("%s covariance matrix __radd__ method not available for %s type!"%(self.__name,type(other)))
608 def __sub__(self, other):
610 return self.__B - numpy.asmatrix(other)
611 elif self.isvector() or self.isscalar():
612 _A = numpy.asarray(other)
613 _A.reshape(_A.size)[::_A.shape[1]+1] = self.__B - _A.reshape(_A.size)[::_A.shape[1]+1]
614 return numpy.asmatrix(_A)
616 def __rsub__(self, other):
617 raise NotImplementedError("%s covariance matrix __rsub__ method not available for %s type!"%(self.__name,type(other)))
622 def __mul__(self, other):
623 if self.ismatrix() and isinstance(other,numpy.matrix):
624 return self.__B * other
625 elif self.ismatrix() and (isinstance(other,numpy.ndarray) \
626 or isinstance(other,list) \
627 or isinstance(other,tuple)):
628 if numpy.ravel(other).size == self.shape[1]: # Vecteur
629 return self.__B * numpy.asmatrix(numpy.ravel(other)).T
630 elif numpy.asmatrix(other).shape[0] == self.shape[1]: # Matrice
631 return self.__B * numpy.asmatrix(other)
633 raise ValueError("operands could not be broadcast together with shapes %s %s in %s matrix"%(self.shape,numpy.asmatrix(other).shape,self.__name))
634 elif self.isvector() and (isinstance(other,numpy.matrix) \
635 or isinstance(other,numpy.ndarray) \
636 or isinstance(other,list) \
637 or isinstance(other,tuple)):
638 if numpy.ravel(other).size == self.shape[1]: # Vecteur
639 return numpy.asmatrix(self.__B * numpy.ravel(other)).T
640 elif numpy.asmatrix(other).shape[0] == self.shape[1]: # Matrice
641 return numpy.asmatrix((self.__B * (numpy.asarray(other).transpose())).transpose())
643 raise ValueError("operands could not be broadcast together with shapes %s %s in %s matrix"%(self.shape,numpy.ravel(other).shape,self.__name))
644 elif self.isscalar() and isinstance(other,numpy.matrix):
645 return self.__B * other
646 elif self.isscalar() and (isinstance(other,numpy.ndarray) \
647 or isinstance(other,list) \
648 or isinstance(other,tuple)):
649 if len(numpy.asarray(other).shape) == 1 or numpy.asarray(other).shape[1] == 1 or numpy.asarray(other).shape[0] == 1:
650 return self.__B * numpy.asmatrix(numpy.ravel(other)).T
652 return self.__B * numpy.asmatrix(other)
654 raise NotImplementedError("%s covariance matrix __mul__ method not available for %s type!"%(self.__name,type(other)))
656 def __rmul__(self, other):
657 if self.ismatrix() and isinstance(other,numpy.matrix):
658 return other * self.__B
659 elif self.isvector() and isinstance(other,numpy.matrix):
660 if numpy.ravel(other).size == self.shape[0]: # Vecteur
661 return numpy.asmatrix(numpy.ravel(other) * self.__B)
662 elif numpy.asmatrix(other).shape[1] == self.shape[0]: # Matrice
663 return numpy.asmatrix(numpy.array(other) * self.__B)
665 raise ValueError("operands could not be broadcast together with shapes %s %s in %s matrix"%(self.shape,numpy.ravel(other).shape,self.__name))
666 elif self.isscalar() and isinstance(other,numpy.matrix):
667 return other * self.__B
669 raise NotImplementedError("%s covariance matrix __rmul__ method not available for %s type!"%(self.__name,type(other)))
674 # ==============================================================================
675 if __name__ == "__main__":
676 print '\n AUTODIAGNOSTIC \n'