1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2008-2024 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 __author__ = "Jean-Philippe ARGAUD"
36 from functools import partial
37 from daCore import Persistence
38 from daCore import PlatformInfo
39 from daCore import Interfaces
40 from daCore import Templates
42 # ==============================================================================
43 class CacheManager(object):
45 Classe générale de gestion d'un cache de calculs
48 "__tolerBP", "__lengthOR", "__initlnOR", "__seenNames", "__enabled",
53 toleranceInRedundancy = 1.e-18,
54 lengthOfRedundancy = -1,
57 Les caractéristiques de tolérance peuvent être modifiées à la création.
59 self.__tolerBP = float(toleranceInRedundancy)
60 self.__lengthOR = int(lengthOfRedundancy)
61 self.__initlnOR = self.__lengthOR
71 def wasCalculatedIn(self, xValue, oName="" ):
72 "Vérifie l'existence d'un calcul correspondant à la valeur"
76 for i in range(min(len(self.__listOPCV),self.__lengthOR)-1,-1,-1):
77 if not hasattr(xValue, 'size'):
79 elif (str(oName) != self.__listOPCV[i][3]):
81 elif (xValue.size != self.__listOPCV[i][0].size):
83 elif (numpy.ravel(xValue)[0] - self.__listOPCV[i][0][0]) > (self.__tolerBP * self.__listOPCV[i][2] / self.__listOPCV[i][0].size):
85 elif numpy.linalg.norm(numpy.ravel(xValue) - self.__listOPCV[i][0]) < (self.__tolerBP * self.__listOPCV[i][2]):
87 __HxV = self.__listOPCV[i][1]
91 def storeValueInX(self, xValue, HxValue, oName="" ):
92 "Stocke pour un opérateur o un calcul Hx correspondant à la valeur x"
93 if self.__lengthOR < 0:
94 self.__lengthOR = 2 * min(numpy.size(xValue), 50) + 2
95 self.__initlnOR = self.__lengthOR
96 self.__seenNames.append(str(oName))
97 if str(oName) not in self.__seenNames: # Etend la liste si nouveau
98 self.__lengthOR += 2 * min(numpy.size(xValue), 50) + 2
99 self.__initlnOR += self.__lengthOR
100 self.__seenNames.append(str(oName))
101 while len(self.__listOPCV) > self.__lengthOR:
102 self.__listOPCV.pop(0)
103 self.__listOPCV.append( (
104 copy.copy(numpy.ravel(xValue)), # 0 Previous point
105 copy.copy(HxValue), # 1 Previous value
106 numpy.linalg.norm(xValue), # 2 Norm
107 str(oName), # 3 Operator name
112 self.__initlnOR = self.__lengthOR
114 self.__enabled = False
118 self.__lengthOR = self.__initlnOR
119 self.__enabled = True
121 # ==============================================================================
122 class Operator(object):
124 Classe générale d'interface de type opérateur simple
127 "__name", "__NbCallsAsMatrix", "__NbCallsAsMethod",
128 "__NbCallsOfCached", "__reduceM", "__avoidRC", "__inputAsMF",
129 "__mpEnabled", "__extraArgs", "__Method", "__Matrix", "__Type",
138 name = "GenericOperator",
141 avoidingRedundancy = True,
142 reducingMemoryUse = False,
143 inputAsMultiFunction = False,
144 enableMultiProcess = False,
145 extraArguments = None,
148 On construit un objet de ce type en fournissant, à l'aide de l'un des
149 deux mots-clé, soit une fonction ou un multi-fonction python, soit une
152 - name : nom d'opérateur
153 - fromMethod : argument de type fonction Python
154 - fromMatrix : argument adapté au constructeur numpy.array/matrix
155 - avoidingRedundancy : booléen évitant (ou pas) les calculs redondants
156 - reducingMemoryUse : booléen forçant (ou pas) des calculs moins
158 - inputAsMultiFunction : booléen indiquant une fonction explicitement
159 définie (ou pas) en multi-fonction
160 - extraArguments : arguments supplémentaires passés à la fonction de
161 base et ses dérivées (tuple ou dictionnaire)
163 self.__name = str(name)
164 self.__NbCallsAsMatrix, self.__NbCallsAsMethod, self.__NbCallsOfCached = 0, 0, 0
165 self.__reduceM = bool( reducingMemoryUse )
166 self.__avoidRC = bool( avoidingRedundancy )
167 self.__inputAsMF = bool( inputAsMultiFunction )
168 self.__mpEnabled = bool( enableMultiProcess )
169 self.__extraArgs = extraArguments
170 if fromMethod is not None and self.__inputAsMF:
171 self.__Method = fromMethod # logtimer(fromMethod)
173 self.__Type = "Method"
174 elif fromMethod is not None and not self.__inputAsMF:
175 self.__Method = partial( MultiFonction, _sFunction=fromMethod, _mpEnabled=self.__mpEnabled)
177 self.__Type = "Method"
178 elif fromMatrix is not None:
180 if isinstance(fromMatrix, str):
181 fromMatrix = PlatformInfo.strmatrix2liststr( fromMatrix )
182 self.__Matrix = numpy.asarray( fromMatrix, dtype=float )
183 self.__Type = "Matrix"
189 def disableAvoidingRedundancy(self):
191 Operator.CM.disable()
193 def enableAvoidingRedundancy(self):
198 Operator.CM.disable()
204 def appliedTo(self, xValue, HValue = None, argsAsSerie = False, returnSerieAsArrayMatrix = False):
206 Permet de restituer le résultat de l'application de l'opérateur à une
207 série d'arguments xValue. Cette méthode se contente d'appliquer, chaque
208 argument devant a priori être du bon type.
210 - les arguments par série sont :
211 - xValue : argument adapté pour appliquer l'opérateur
212 - HValue : valeur précalculée de l'opérateur en ce point
213 - argsAsSerie : indique si les arguments sont une mono ou multi-valeur
220 if HValue is not None:
224 PlatformInfo.isIterable( _xValue, True, " in Operator.appliedTo" )
226 if _HValue is not None:
227 assert len(_xValue) == len(_HValue), "Incompatible number of elements in xValue and HValue"
229 for i in range(len(_HValue)):
230 _HxValue.append( _HValue[i] )
232 Operator.CM.storeValueInX(_xValue[i],_HxValue[-1],self.__name)
237 for i, xv in enumerate(_xValue):
239 __alreadyCalculated, __HxV = Operator.CM.wasCalculatedIn(xv,self.__name)
241 __alreadyCalculated = False
243 if __alreadyCalculated:
244 self.__addOneCacheCall()
247 if self.__Matrix is not None:
248 self.__addOneMatrixCall()
249 _hv = self.__Matrix @ numpy.ravel(xv)
251 self.__addOneMethodCall()
255 _HxValue.append( _hv )
257 if len(_xserie)>0 and self.__Matrix is None:
258 if self.__extraArgs is None:
259 _hserie = self.__Method( _xserie ) # Calcul MF
261 _hserie = self.__Method( _xserie, self.__extraArgs ) # Calcul MF
262 if not hasattr(_hserie, "pop"):
264 "The user input multi-function doesn't seem to return a"+\
265 " result sequence, behaving like a mono-function. It has"+\
273 Operator.CM.storeValueInX(_xv,_hv,self.__name)
275 if returnSerieAsArrayMatrix:
276 _HxValue = numpy.stack([numpy.ravel(_hv) for _hv in _HxValue], axis=1)
278 if argsAsSerie: return _HxValue
279 else: return _HxValue[-1]
281 def appliedControledFormTo(self, paires, argsAsSerie = False, returnSerieAsArrayMatrix = False):
283 Permet de restituer le résultat de l'application de l'opérateur à des
284 paires (xValue, uValue). Cette méthode se contente d'appliquer, son
285 argument devant a priori être du bon type. Si la uValue est None,
286 on suppose que l'opérateur ne s'applique qu'à xValue.
288 - paires : les arguments par paire sont :
289 - xValue : argument X adapté pour appliquer l'opérateur
290 - uValue : argument U adapté pour appliquer l'opérateur
291 - argsAsSerie : indique si l'argument est une mono ou multi-valeur
293 if argsAsSerie: _xuValue = paires
294 else: _xuValue = (paires,)
295 PlatformInfo.isIterable( _xuValue, True, " in Operator.appliedControledFormTo" )
297 if self.__Matrix is not None:
299 for paire in _xuValue:
300 _xValue, _uValue = paire
301 self.__addOneMatrixCall()
302 _HxValue.append( self.__Matrix @ numpy.ravel(_xValue) )
305 for paire in _xuValue:
306 _xValue, _uValue = paire
307 if _uValue is not None:
308 _xuArgs.append( paire )
310 _xuArgs.append( _xValue )
311 self.__addOneMethodCall( len(_xuArgs) )
312 if self.__extraArgs is None:
313 _HxValue = self.__Method( _xuArgs ) # Calcul MF
315 _HxValue = self.__Method( _xuArgs, self.__extraArgs ) # Calcul MF
317 if returnSerieAsArrayMatrix:
318 _HxValue = numpy.stack([numpy.ravel(_hv) for _hv in _HxValue], axis=1)
320 if argsAsSerie: return _HxValue
321 else: return _HxValue[-1]
323 def appliedInXTo(self, paires, argsAsSerie = False, returnSerieAsArrayMatrix = False):
325 Permet de restituer le résultat de l'application de l'opérateur à une
326 série d'arguments xValue, sachant que l'opérateur est valable en
327 xNominal. Cette méthode se contente d'appliquer, son argument devant a
328 priori être du bon type. Si l'opérateur est linéaire car c'est une
329 matrice, alors il est valable en tout point nominal et xNominal peut
330 être quelconque. Il n'y a qu'une seule paire par défaut, et argsAsSerie
331 permet d'indiquer que l'argument est multi-paires.
333 - paires : les arguments par paire sont :
334 - xNominal : série d'arguments permettant de donner le point où
335 l'opérateur est construit pour être ensuite appliqué
336 - xValue : série d'arguments adaptés pour appliquer l'opérateur
337 - argsAsSerie : indique si l'argument est une mono ou multi-valeur
339 if argsAsSerie: _nxValue = paires
340 else: _nxValue = (paires,)
341 PlatformInfo.isIterable( _nxValue, True, " in Operator.appliedInXTo" )
343 if self.__Matrix is not None:
345 for paire in _nxValue:
346 _xNominal, _xValue = paire
347 self.__addOneMatrixCall()
348 _HxValue.append( self.__Matrix @ numpy.ravel(_xValue) )
350 self.__addOneMethodCall( len(_nxValue) )
351 if self.__extraArgs is None:
352 _HxValue = self.__Method( _nxValue ) # Calcul MF
354 _HxValue = self.__Method( _nxValue, self.__extraArgs ) # Calcul MF
356 if returnSerieAsArrayMatrix:
357 _HxValue = numpy.stack([numpy.ravel(_hv) for _hv in _HxValue], axis=1)
359 if argsAsSerie: return _HxValue
360 else: return _HxValue[-1]
362 def asMatrix(self, ValueForMethodForm = "UnknownVoidValue", argsAsSerie = False):
364 Permet de renvoyer l'opérateur sous la forme d'une matrice
366 if self.__Matrix is not None:
367 self.__addOneMatrixCall()
368 mValue = [self.__Matrix,]
369 elif not isinstance(ValueForMethodForm,str) or ValueForMethodForm != "UnknownVoidValue": # Ne pas utiliser "None"
372 self.__addOneMethodCall( len(ValueForMethodForm) )
373 for _vfmf in ValueForMethodForm:
374 mValue.append( self.__Method(((_vfmf, None),)) )
376 self.__addOneMethodCall()
377 mValue = self.__Method(((ValueForMethodForm, None),))
379 raise ValueError("Matrix form of the operator defined as a function/method requires to give an operating point.")
381 if argsAsSerie: return mValue
382 else: return mValue[-1]
386 Renvoie la taille sous forme numpy si l'opérateur est disponible sous
387 la forme d'une matrice
389 if self.__Matrix is not None:
390 return self.__Matrix.shape
392 raise ValueError("Matrix form of the operator is not available, nor the shape")
394 def nbcalls(self, which=None):
396 Renvoie les nombres d'évaluations de l'opérateur
399 self.__NbCallsAsMatrix+self.__NbCallsAsMethod,
400 self.__NbCallsAsMatrix,
401 self.__NbCallsAsMethod,
402 self.__NbCallsOfCached,
403 Operator.NbCallsAsMatrix+Operator.NbCallsAsMethod,
404 Operator.NbCallsAsMatrix,
405 Operator.NbCallsAsMethod,
406 Operator.NbCallsOfCached,
408 if which is None: return __nbcalls
409 else: return __nbcalls[which]
411 def __addOneMatrixCall(self):
412 "Comptabilise un appel"
413 self.__NbCallsAsMatrix += 1 # Decompte local
414 Operator.NbCallsAsMatrix += 1 # Decompte global
416 def __addOneMethodCall(self, nb = 1):
417 "Comptabilise un appel"
418 self.__NbCallsAsMethod += nb # Decompte local
419 Operator.NbCallsAsMethod += nb # Decompte global
421 def __addOneCacheCall(self):
422 "Comptabilise un appel"
423 self.__NbCallsOfCached += 1 # Decompte local
424 Operator.NbCallsOfCached += 1 # Decompte global
426 # ==============================================================================
427 class FullOperator(object):
429 Classe générale d'interface de type opérateur complet
430 (Direct, Linéaire Tangent, Adjoint)
433 "__name", "__check", "__extraArgs", "__FO", "__T",
437 name = "GenericFullOperator",
439 asOneFunction = None, # 1 Fonction
440 asThreeFunctions = None, # 3 Fonctions in a dictionary
441 asScript = None, # 1 or 3 Fonction(s) by script
442 asDict = None, # Parameters
444 extraArguments = None,
445 performancePrf = None,
446 inputAsMF = False,# Fonction(s) as Multi-Functions
451 self.__name = str(name)
452 self.__check = bool(toBeChecked)
453 self.__extraArgs = extraArguments
458 if (asDict is not None) and isinstance(asDict, dict):
459 __Parameters.update( asDict )
460 # Priorité à EnableMultiProcessingInDerivatives=True
461 if "EnableMultiProcessing" in __Parameters and __Parameters["EnableMultiProcessing"]:
462 __Parameters["EnableMultiProcessingInDerivatives"] = True
463 __Parameters["EnableMultiProcessingInEvaluation"] = False
464 if "EnableMultiProcessingInDerivatives" not in __Parameters:
465 __Parameters["EnableMultiProcessingInDerivatives"] = False
466 if __Parameters["EnableMultiProcessingInDerivatives"]:
467 __Parameters["EnableMultiProcessingInEvaluation"] = False
468 if "EnableMultiProcessingInEvaluation" not in __Parameters:
469 __Parameters["EnableMultiProcessingInEvaluation"] = False
470 if "withIncrement" in __Parameters: # Temporaire
471 __Parameters["DifferentialIncrement"] = __Parameters["withIncrement"]
472 # Le défaut est équivalent à "ReducedOverallRequirements"
473 __reduceM, __avoidRC = True, True
474 if performancePrf is not None:
475 if performancePrf == "ReducedAmountOfCalculation":
476 __reduceM, __avoidRC = False, True
477 elif performancePrf == "ReducedMemoryFootprint":
478 __reduceM, __avoidRC = True, False
479 elif performancePrf == "NoSavings":
480 __reduceM, __avoidRC = False, False
482 if asScript is not None:
483 __Matrix, __Function = None, None
485 __Matrix = Interfaces.ImportFromScript(asScript).getvalue( self.__name )
487 __Function = { "Direct":Interfaces.ImportFromScript(asScript).getvalue( "DirectOperator" ) }
488 __Function.update({"useApproximatedDerivatives":True})
489 __Function.update(__Parameters)
490 elif asThreeFunctions:
492 "Direct" :Interfaces.ImportFromScript(asScript).getvalue( "DirectOperator" ),
493 "Tangent":Interfaces.ImportFromScript(asScript).getvalue( "TangentOperator" ),
494 "Adjoint":Interfaces.ImportFromScript(asScript).getvalue( "AdjointOperator" ),
496 __Function.update(__Parameters)
499 if asOneFunction is not None:
500 if isinstance(asOneFunction, dict) and "Direct" in asOneFunction:
501 if asOneFunction["Direct"] is not None:
502 __Function = asOneFunction
504 raise ValueError("The function has to be given in a dictionnary which have 1 key (\"Direct\")")
506 __Function = { "Direct":asOneFunction }
507 __Function.update({"useApproximatedDerivatives":True})
508 __Function.update(__Parameters)
509 elif asThreeFunctions is not None:
510 if isinstance(asThreeFunctions, dict) and \
511 ("Tangent" in asThreeFunctions) and (asThreeFunctions["Tangent"] is not None) and \
512 ("Adjoint" in asThreeFunctions) and (asThreeFunctions["Adjoint"] is not None) and \
513 (("useApproximatedDerivatives" not in asThreeFunctions) or not bool(asThreeFunctions["useApproximatedDerivatives"])):
514 __Function = asThreeFunctions
515 elif isinstance(asThreeFunctions, dict) and \
516 ("Direct" in asThreeFunctions) and (asThreeFunctions["Direct"] is not None):
517 __Function = asThreeFunctions
518 __Function.update({"useApproximatedDerivatives":True})
521 "The functions has to be given in a dictionnary which have either"+\
522 " 1 key (\"Direct\") or"+\
523 " 3 keys (\"Direct\" (optionnal), \"Tangent\" and \"Adjoint\")")
524 if "Direct" not in asThreeFunctions:
525 __Function["Direct"] = asThreeFunctions["Tangent"]
526 __Function.update(__Parameters)
530 if appliedInX is not None and isinstance(appliedInX, dict):
531 __appliedInX = appliedInX
532 elif appliedInX is not None:
533 __appliedInX = {"HXb":appliedInX}
537 if scheduledBy is not None:
538 self.__T = scheduledBy
540 if isinstance(__Function, dict) and \
541 ("useApproximatedDerivatives" in __Function) and bool(__Function["useApproximatedDerivatives"]) and \
542 ("Direct" in __Function) and (__Function["Direct"] is not None):
543 if "CenteredFiniteDifference" not in __Function: __Function["CenteredFiniteDifference"] = False
544 if "DifferentialIncrement" not in __Function: __Function["DifferentialIncrement"] = 0.01
545 if "withdX" not in __Function: __Function["withdX"] = None
546 if "withReducingMemoryUse" not in __Function: __Function["withReducingMemoryUse"] = __reduceM
547 if "withAvoidingRedundancy" not in __Function: __Function["withAvoidingRedundancy"] = __avoidRC
548 if "withToleranceInRedundancy" not in __Function: __Function["withToleranceInRedundancy"] = 1.e-18
549 if "withLengthOfRedundancy" not in __Function: __Function["withLengthOfRedundancy"] = -1
550 if "NumberOfProcesses" not in __Function: __Function["NumberOfProcesses"] = None
551 if "withmfEnabled" not in __Function: __Function["withmfEnabled"] = inputAsMF
552 from daCore import NumericObjects
553 FDA = NumericObjects.FDApproximation(
555 Function = __Function["Direct"],
556 centeredDF = __Function["CenteredFiniteDifference"],
557 increment = __Function["DifferentialIncrement"],
558 dX = __Function["withdX"],
559 extraArguments = self.__extraArgs,
560 reducingMemoryUse = __Function["withReducingMemoryUse"],
561 avoidingRedundancy = __Function["withAvoidingRedundancy"],
562 toleranceInRedundancy = __Function["withToleranceInRedundancy"],
563 lengthOfRedundancy = __Function["withLengthOfRedundancy"],
564 mpEnabled = __Function["EnableMultiProcessingInDerivatives"],
565 mpWorkers = __Function["NumberOfProcesses"],
566 mfEnabled = __Function["withmfEnabled"],
568 self.__FO["Direct"] = Operator(
570 fromMethod = FDA.DirectOperator,
571 reducingMemoryUse = __reduceM,
572 avoidingRedundancy = __avoidRC,
573 inputAsMultiFunction = inputAsMF,
574 extraArguments = self.__extraArgs,
575 enableMultiProcess = __Parameters["EnableMultiProcessingInEvaluation"] )
576 self.__FO["Tangent"] = Operator(
577 name = self.__name+"Tangent",
578 fromMethod = FDA.TangentOperator,
579 reducingMemoryUse = __reduceM,
580 avoidingRedundancy = __avoidRC,
581 inputAsMultiFunction = inputAsMF,
582 extraArguments = self.__extraArgs )
583 self.__FO["Adjoint"] = Operator(
584 name = self.__name+"Adjoint",
585 fromMethod = FDA.AdjointOperator,
586 reducingMemoryUse = __reduceM,
587 avoidingRedundancy = __avoidRC,
588 inputAsMultiFunction = inputAsMF,
589 extraArguments = self.__extraArgs )
590 self.__FO["DifferentialIncrement"] = __Function["DifferentialIncrement"]
591 elif isinstance(__Function, dict) and \
592 ("Direct" in __Function) and ("Tangent" in __Function) and ("Adjoint" in __Function) and \
593 (__Function["Direct"] is not None) and (__Function["Tangent"] is not None) and (__Function["Adjoint"] is not None):
594 self.__FO["Direct"] = Operator(
596 fromMethod = __Function["Direct"],
597 reducingMemoryUse = __reduceM,
598 avoidingRedundancy = __avoidRC,
599 inputAsMultiFunction = inputAsMF,
600 extraArguments = self.__extraArgs,
601 enableMultiProcess = __Parameters["EnableMultiProcessingInEvaluation"] )
602 self.__FO["Tangent"] = Operator(
603 name = self.__name+"Tangent",
604 fromMethod = __Function["Tangent"],
605 reducingMemoryUse = __reduceM,
606 avoidingRedundancy = __avoidRC,
607 inputAsMultiFunction = inputAsMF,
608 extraArguments = self.__extraArgs )
609 self.__FO["Adjoint"] = Operator(
610 name = self.__name+"Adjoint",
611 fromMethod = __Function["Adjoint"],
612 reducingMemoryUse = __reduceM,
613 avoidingRedundancy = __avoidRC,
614 inputAsMultiFunction = inputAsMF,
615 extraArguments = self.__extraArgs )
616 self.__FO["DifferentialIncrement"] = None
617 elif asMatrix is not None:
618 if isinstance(__Matrix, str):
619 __Matrix = PlatformInfo.strmatrix2liststr( __Matrix )
620 __matrice = numpy.asarray( __Matrix, dtype=float )
621 self.__FO["Direct"] = Operator(
623 fromMatrix = __matrice,
624 reducingMemoryUse = __reduceM,
625 avoidingRedundancy = __avoidRC,
626 inputAsMultiFunction = inputAsMF,
627 enableMultiProcess = __Parameters["EnableMultiProcessingInEvaluation"] )
628 self.__FO["Tangent"] = Operator(
629 name = self.__name+"Tangent",
630 fromMatrix = __matrice,
631 reducingMemoryUse = __reduceM,
632 avoidingRedundancy = __avoidRC,
633 inputAsMultiFunction = inputAsMF )
634 self.__FO["Adjoint"] = Operator(
635 name = self.__name+"Adjoint",
636 fromMatrix = __matrice.T,
637 reducingMemoryUse = __reduceM,
638 avoidingRedundancy = __avoidRC,
639 inputAsMultiFunction = inputAsMF )
641 self.__FO["DifferentialIncrement"] = None
644 "The %s object is improperly defined or undefined,"%self.__name+\
645 " it requires at minima either a matrix, a Direct operator for"+\
646 " approximate derivatives or a Tangent/Adjoint operators pair."+\
647 " Please check your operator input.")
649 if __appliedInX is not None:
650 self.__FO["AppliedInX"] = {}
651 for key in __appliedInX:
652 if isinstance(__appliedInX[key], str):
653 __appliedInX[key] = PlatformInfo.strvect2liststr( __appliedInX[key] )
654 self.__FO["AppliedInX"][key] = numpy.ravel( __appliedInX[key] ).reshape((-1,1))
656 self.__FO["AppliedInX"] = None
662 "x.__repr__() <==> repr(x)"
663 return repr(self.__FO)
666 "x.__str__() <==> str(x)"
667 return str(self.__FO)
669 # ==============================================================================
670 class Algorithm(object):
672 Classe générale d'interface de type algorithme
674 Elle donne un cadre pour l'écriture d'une classe élémentaire d'algorithme
675 d'assimilation, en fournissant un container (dictionnaire) de variables
676 persistantes initialisées, et des méthodes d'accès à ces variables stockées.
678 Une classe élémentaire d'algorithme doit implémenter la méthode "run".
681 "_name", "_parameters", "__internal_state", "__required_parameters",
682 "_m", "__variable_names_not_public", "__canonical_parameter_name",
683 "__canonical_stored_name", "__replace_by_the_new_name",
687 def __init__(self, name):
689 L'initialisation présente permet de fabriquer des variables de stockage
690 disponibles de manière générique dans les algorithmes élémentaires. Ces
691 variables de stockage sont ensuite conservées dans un dictionnaire
692 interne à l'objet, mais auquel on accède par la méthode "get".
694 Les variables prévues sont :
695 - APosterioriCorrelations : matrice de corrélations de la matrice A
696 - APosterioriCovariance : matrice de covariances a posteriori : A
697 - APosterioriStandardDeviations : vecteur des écart-types de la matrice A
698 - APosterioriVariances : vecteur des variances de la matrice A
699 - Analysis : vecteur d'analyse : Xa
700 - BMA : Background moins Analysis : Xa - Xb
701 - CostFunctionJ : fonction-coût globale, somme des deux parties suivantes Jb et Jo
702 - CostFunctionJAtCurrentOptimum : fonction-coût globale à l'état optimal courant lors d'itérations
703 - CostFunctionJb : partie ébauche ou background de la fonction-coût : Jb
704 - CostFunctionJbAtCurrentOptimum : partie ébauche à l'état optimal courant lors d'itérations
705 - CostFunctionJo : partie observations de la fonction-coût : Jo
706 - CostFunctionJoAtCurrentOptimum : partie observations à l'état optimal courant lors d'itérations
707 - CurrentIterationNumber : numéro courant d'itération dans les algorithmes itératifs, à partir de 0
708 - CurrentOptimum : état optimal courant lors d'itérations
709 - CurrentState : état courant lors d'itérations
710 - CurrentStepNumber : pas courant d'avancement dans les algorithmes en évolution, à partir de 0
711 - EnsembleOfSimulations : ensemble d'états (sorties, simulations) rangés par colonne dans une matrice
712 - EnsembleOfSnapshots : ensemble d'états rangés par colonne dans une matrice
713 - EnsembleOfStates : ensemble d'états (entrées, paramètres) rangés par colonne dans une matrice
714 - ForecastCovariance : covariance de l'état prédit courant lors d'itérations
715 - ForecastState : état prédit courant lors d'itérations
716 - GradientOfCostFunctionJ : gradient de la fonction-coût globale
717 - GradientOfCostFunctionJb : gradient de la partie ébauche de la fonction-coût
718 - GradientOfCostFunctionJo : gradient de la partie observations de la fonction-coût
719 - IndexOfOptimum : index de l'état optimal courant lors d'itérations
720 - Innovation : l'innovation : d = Y - H(X)
721 - InnovationAtCurrentAnalysis : l'innovation à l'état analysé : da = Y - H(Xa)
722 - InnovationAtCurrentState : l'innovation à l'état courant : dn = Y - H(Xn)
723 - InternalCostFunctionJ : ensemble de valeurs internes de fonction-coût J dans un vecteur
724 - InternalCostFunctionJb : ensemble de valeurs internes de fonction-coût Jb dans un vecteur
725 - InternalCostFunctionJb : ensemble de valeurs internes de fonction-coût Jo dans un vecteur
726 - InternalStates : ensemble d'états internes rangés par colonne dans une matrice (=EnsembleOfSnapshots)
727 - JacobianMatrixAtBackground : matrice jacobienne à l'état d'ébauche
728 - JacobianMatrixAtCurrentState : matrice jacobienne à l'état courant
729 - JacobianMatrixAtOptimum : matrice jacobienne à l'optimum
730 - KalmanGainAtOptimum : gain de Kalman à l'optimum
731 - MahalanobisConsistency : indicateur de consistance des covariances
732 - OMA : Observation moins Analyse : Y - Xa
733 - OMB : Observation moins Background : Y - Xb
734 - ReducedCoordinates : coordonnées dans la base réduite
735 - Residu : dans le cas des algorithmes de vérification
736 - SampledStateForQuantiles : échantillons d'états pour l'estimation des quantiles
737 - SigmaBck2 : indicateur de correction optimale des erreurs d'ébauche
738 - SigmaObs2 : indicateur de correction optimale des erreurs d'observation
739 - SimulatedObservationAtBackground : l'état observé H(Xb) à l'ébauche
740 - SimulatedObservationAtCurrentOptimum : l'état observé H(X) à l'état optimal courant
741 - SimulatedObservationAtCurrentState : l'état observé H(X) à l'état courant
742 - SimulatedObservationAtOptimum : l'état observé H(Xa) à l'optimum
743 - SimulationQuantiles : états observés H(X) pour les quantiles demandés
744 - SingularValues : valeurs singulières provenant d'une décomposition SVD
745 On peut rajouter des variables à stocker dans l'initialisation de
746 l'algorithme élémentaire qui va hériter de cette classe
748 logging.debug("%s Initialisation", str(name))
749 self._m = PlatformInfo.SystemUsage()
751 self._name = str( name )
752 self._parameters = {"StoreSupplementaryCalculations":[]}
753 self.__internal_state = {}
754 self.__required_parameters = {}
755 self.__required_inputs = {
756 "RequiredInputValues":{"mandatory":(), "optional":()},
757 "ClassificationTags":[],
759 self.__variable_names_not_public = {"nextStep":False} # Duplication dans AlgorithmAndParameters
760 self.__canonical_parameter_name = {} # Correspondance "lower"->"correct"
761 self.__canonical_stored_name = {} # Correspondance "lower"->"correct"
762 self.__replace_by_the_new_name = {} # Nouveau nom à partir d'un nom ancien
764 self.StoredVariables = {}
765 self.StoredVariables["APosterioriCorrelations"] = Persistence.OneMatrix(name = "APosterioriCorrelations")
766 self.StoredVariables["APosterioriCovariance"] = Persistence.OneMatrix(name = "APosterioriCovariance")
767 self.StoredVariables["APosterioriStandardDeviations"] = Persistence.OneVector(name = "APosterioriStandardDeviations")
768 self.StoredVariables["APosterioriVariances"] = Persistence.OneVector(name = "APosterioriVariances")
769 self.StoredVariables["Analysis"] = Persistence.OneVector(name = "Analysis")
770 self.StoredVariables["BMA"] = Persistence.OneVector(name = "BMA")
771 self.StoredVariables["CostFunctionJ"] = Persistence.OneScalar(name = "CostFunctionJ")
772 self.StoredVariables["CostFunctionJAtCurrentOptimum"] = Persistence.OneScalar(name = "CostFunctionJAtCurrentOptimum")
773 self.StoredVariables["CostFunctionJb"] = Persistence.OneScalar(name = "CostFunctionJb")
774 self.StoredVariables["CostFunctionJbAtCurrentOptimum"] = Persistence.OneScalar(name = "CostFunctionJbAtCurrentOptimum")
775 self.StoredVariables["CostFunctionJo"] = Persistence.OneScalar(name = "CostFunctionJo")
776 self.StoredVariables["CostFunctionJoAtCurrentOptimum"] = Persistence.OneScalar(name = "CostFunctionJoAtCurrentOptimum")
777 self.StoredVariables["CurrentEnsembleState"] = Persistence.OneMatrix(name = "CurrentEnsembleState")
778 self.StoredVariables["CurrentIterationNumber"] = Persistence.OneIndex(name = "CurrentIterationNumber")
779 self.StoredVariables["CurrentOptimum"] = Persistence.OneVector(name = "CurrentOptimum")
780 self.StoredVariables["CurrentState"] = Persistence.OneVector(name = "CurrentState")
781 self.StoredVariables["CurrentStepNumber"] = Persistence.OneIndex(name = "CurrentStepNumber")
782 self.StoredVariables["EnsembleOfSimulations"] = Persistence.OneMatrice(name = "EnsembleOfSimulations")
783 self.StoredVariables["EnsembleOfSnapshots"] = Persistence.OneMatrice(name = "EnsembleOfSnapshots")
784 self.StoredVariables["EnsembleOfStates"] = Persistence.OneMatrice(name = "EnsembleOfStates")
785 self.StoredVariables["ExcludedPoints"] = Persistence.OneVector(name = "ExcludedPoints")
786 self.StoredVariables["ForecastCovariance"] = Persistence.OneMatrix(name = "ForecastCovariance")
787 self.StoredVariables["ForecastState"] = Persistence.OneVector(name = "ForecastState")
788 self.StoredVariables["GradientOfCostFunctionJ"] = Persistence.OneVector(name = "GradientOfCostFunctionJ")
789 self.StoredVariables["GradientOfCostFunctionJb"] = Persistence.OneVector(name = "GradientOfCostFunctionJb")
790 self.StoredVariables["GradientOfCostFunctionJo"] = Persistence.OneVector(name = "GradientOfCostFunctionJo")
791 self.StoredVariables["IndexOfOptimum"] = Persistence.OneIndex(name = "IndexOfOptimum")
792 self.StoredVariables["Innovation"] = Persistence.OneVector(name = "Innovation")
793 self.StoredVariables["InnovationAtCurrentAnalysis"] = Persistence.OneVector(name = "InnovationAtCurrentAnalysis")
794 self.StoredVariables["InnovationAtCurrentState"] = Persistence.OneVector(name = "InnovationAtCurrentState")
795 self.StoredVariables["InternalCostFunctionJ"] = Persistence.OneVector(name = "InternalCostFunctionJ")
796 self.StoredVariables["InternalCostFunctionJb"] = Persistence.OneVector(name = "InternalCostFunctionJb")
797 self.StoredVariables["InternalCostFunctionJo"] = Persistence.OneVector(name = "InternalCostFunctionJo")
798 self.StoredVariables["InternalStates"] = Persistence.OneMatrix(name = "InternalStates")
799 self.StoredVariables["JacobianMatrixAtBackground"] = Persistence.OneMatrix(name = "JacobianMatrixAtBackground")
800 self.StoredVariables["JacobianMatrixAtCurrentState"] = Persistence.OneMatrix(name = "JacobianMatrixAtCurrentState")
801 self.StoredVariables["JacobianMatrixAtOptimum"] = Persistence.OneMatrix(name = "JacobianMatrixAtOptimum")
802 self.StoredVariables["KalmanGainAtOptimum"] = Persistence.OneMatrix(name = "KalmanGainAtOptimum")
803 self.StoredVariables["MahalanobisConsistency"] = Persistence.OneScalar(name = "MahalanobisConsistency")
804 self.StoredVariables["OMA"] = Persistence.OneVector(name = "OMA")
805 self.StoredVariables["OMB"] = Persistence.OneVector(name = "OMB")
806 self.StoredVariables["OptimalPoints"] = Persistence.OneVector(name = "OptimalPoints")
807 self.StoredVariables["ReducedBasis"] = Persistence.OneMatrix(name = "ReducedBasis")
808 self.StoredVariables["ReducedCoordinates"] = Persistence.OneVector(name = "ReducedCoordinates")
809 self.StoredVariables["Residu"] = Persistence.OneScalar(name = "Residu")
810 self.StoredVariables["Residus"] = Persistence.OneVector(name = "Residus")
811 self.StoredVariables["SampledStateForQuantiles"] = Persistence.OneMatrix(name = "SampledStateForQuantiles")
812 self.StoredVariables["SigmaBck2"] = Persistence.OneScalar(name = "SigmaBck2")
813 self.StoredVariables["SigmaObs2"] = Persistence.OneScalar(name = "SigmaObs2")
814 self.StoredVariables["SimulatedObservationAtBackground"] = Persistence.OneVector(name = "SimulatedObservationAtBackground")
815 self.StoredVariables["SimulatedObservationAtCurrentAnalysis"]= Persistence.OneVector(name = "SimulatedObservationAtCurrentAnalysis")
816 self.StoredVariables["SimulatedObservationAtCurrentOptimum"] = Persistence.OneVector(name = "SimulatedObservationAtCurrentOptimum")
817 self.StoredVariables["SimulatedObservationAtCurrentState"] = Persistence.OneVector(name = "SimulatedObservationAtCurrentState")
818 self.StoredVariables["SimulatedObservationAtOptimum"] = Persistence.OneVector(name = "SimulatedObservationAtOptimum")
819 self.StoredVariables["SimulationQuantiles"] = Persistence.OneMatrix(name = "SimulationQuantiles")
820 self.StoredVariables["SingularValues"] = Persistence.OneVector(name = "SingularValues")
822 for k in self.StoredVariables:
823 self.__canonical_stored_name[k.lower()] = k
825 for k, v in self.__variable_names_not_public.items():
826 self.__canonical_parameter_name[k.lower()] = k
827 self.__canonical_parameter_name["algorithm"] = "Algorithm"
828 self.__canonical_parameter_name["storesupplementarycalculations"] = "StoreSupplementaryCalculations"
830 def _pre_run(self, Parameters, Xb=None, Y=None, U=None, HO=None, EM=None, CM=None, R=None, B=None, Q=None ):
832 logging.debug("%s Lancement", self._name)
833 logging.debug("%s Taille mémoire utilisée de %.0f Mio"%(self._name, self._m.getUsedMemory("Mio")))
834 self._getTimeState(reset=True)
836 # Mise à jour des paramètres internes avec le contenu de Parameters, en
837 # reprenant les valeurs par défauts pour toutes celles non définies
838 self.__setParameters(Parameters, reset=True) # Copie
839 for k, v in self.__variable_names_not_public.items():
840 if k not in self._parameters: self.__setParameters( {k:v} )
842 # Corrections et compléments des vecteurs
843 def __test_vvalue(argument, variable, argname, symbol=None):
844 if symbol is None: symbol = variable
846 if variable in self.__required_inputs["RequiredInputValues"]["mandatory"]:
847 raise ValueError("%s %s vector %s is not set and has to be properly defined!"%(self._name,argname,symbol))
848 elif variable in self.__required_inputs["RequiredInputValues"]["optional"]:
849 logging.debug("%s %s vector %s is not set, but is optional."%(self._name,argname,symbol))
851 logging.debug("%s %s vector %s is not set, but is not required."%(self._name,argname,symbol))
853 if variable in self.__required_inputs["RequiredInputValues"]["mandatory"]:
855 "%s %s vector %s is required and set, and its size is %i."%(
856 self._name,argname,symbol,numpy.array(argument).size))
857 elif variable in self.__required_inputs["RequiredInputValues"]["optional"]:
859 "%s %s vector %s is optional and set, and its size is %i."%(
860 self._name,argname,symbol,numpy.array(argument).size))
863 "%s %s vector %s is set although neither required nor optional, and its size is %i."%(
864 self._name,argname,symbol,numpy.array(argument).size))
866 __test_vvalue( Xb, "Xb", "Background or initial state" )
867 __test_vvalue( Y, "Y", "Observation" )
868 __test_vvalue( U, "U", "Control" )
870 # Corrections et compléments des covariances
871 def __test_cvalue(argument, variable, argname, symbol=None):
872 if symbol is None: symbol = variable
874 if variable in self.__required_inputs["RequiredInputValues"]["mandatory"]:
875 raise ValueError("%s %s error covariance matrix %s is not set and has to be properly defined!"%(self._name,argname,symbol))
876 elif variable in self.__required_inputs["RequiredInputValues"]["optional"]:
877 logging.debug("%s %s error covariance matrix %s is not set, but is optional."%(self._name,argname,symbol))
879 logging.debug("%s %s error covariance matrix %s is not set, but is not required."%(self._name,argname,symbol))
881 if variable in self.__required_inputs["RequiredInputValues"]["mandatory"]:
882 logging.debug("%s %s error covariance matrix %s is required and set."%(self._name,argname,symbol))
883 elif variable in self.__required_inputs["RequiredInputValues"]["optional"]:
884 logging.debug("%s %s error covariance matrix %s is optional and set."%(self._name,argname,symbol))
887 "%s %s error covariance matrix %s is set although neither required nor optional."%(
888 self._name,argname,symbol))
890 __test_cvalue( B, "B", "Background" )
891 __test_cvalue( R, "R", "Observation" )
892 __test_cvalue( Q, "Q", "Evolution" )
894 # Corrections et compléments des opérateurs
895 def __test_ovalue(argument, variable, argname, symbol=None):
896 if symbol is None: symbol = variable
897 if argument is None or (isinstance(argument,dict) and len(argument)==0):
898 if variable in self.__required_inputs["RequiredInputValues"]["mandatory"]:
899 raise ValueError("%s %s operator %s is not set and has to be properly defined!"%(self._name,argname,symbol))
900 elif variable in self.__required_inputs["RequiredInputValues"]["optional"]:
901 logging.debug("%s %s operator %s is not set, but is optional."%(self._name,argname,symbol))
903 logging.debug("%s %s operator %s is not set, but is not required."%(self._name,argname,symbol))
905 if variable in self.__required_inputs["RequiredInputValues"]["mandatory"]:
906 logging.debug("%s %s operator %s is required and set."%(self._name,argname,symbol))
907 elif variable in self.__required_inputs["RequiredInputValues"]["optional"]:
908 logging.debug("%s %s operator %s is optional and set."%(self._name,argname,symbol))
910 logging.debug("%s %s operator %s is set although neither required nor optional."%(self._name,argname,symbol))
912 __test_ovalue( HO, "HO", "Observation", "H" )
913 __test_ovalue( EM, "EM", "Evolution", "M" )
914 __test_ovalue( CM, "CM", "Control Model", "C" )
916 # Corrections et compléments des bornes
917 if ("Bounds" in self._parameters) and isinstance(self._parameters["Bounds"], (list, tuple)) and (len(self._parameters["Bounds"]) > 0):
918 logging.debug("%s Bounds taken into account"%(self._name,))
920 self._parameters["Bounds"] = None
921 if ("StateBoundsForQuantiles" in self._parameters) \
922 and isinstance(self._parameters["StateBoundsForQuantiles"], (list, tuple)) \
923 and (len(self._parameters["StateBoundsForQuantiles"]) > 0):
924 logging.debug("%s Bounds for quantiles states taken into account"%(self._name,))
925 # Attention : contrairement à Bounds, pas de défaut à None, sinon on ne peut pas être sans bornes
927 # Corrections et compléments de l'initialisation en X
928 if "InitializationPoint" in self._parameters:
930 if self._parameters["InitializationPoint"] is not None and hasattr(self._parameters["InitializationPoint"],'size'):
931 if self._parameters["InitializationPoint"].size != numpy.ravel(Xb).size:
932 raise ValueError("Incompatible size %i of forced initial point that have to replace the background of size %i" \
933 %(self._parameters["InitializationPoint"].size,numpy.ravel(Xb).size))
934 # Obtenu par typecast : numpy.ravel(self._parameters["InitializationPoint"])
936 self._parameters["InitializationPoint"] = numpy.ravel(Xb)
938 if self._parameters["InitializationPoint"] is None:
939 raise ValueError("Forced initial point can not be set without any given Background or required value")
941 # Correction pour pallier a un bug de TNC sur le retour du Minimum
942 if "Minimizer" in self._parameters and self._parameters["Minimizer"] == "TNC":
943 self.setParameterValue("StoreInternalVariables",True)
945 # Verbosité et logging
946 if logging.getLogger().level < logging.WARNING:
947 self._parameters["optiprint"], self._parameters["optdisp"] = 1, 1
948 self._parameters["optmessages"] = 15
950 self._parameters["optiprint"], self._parameters["optdisp"] = -1, 0
951 self._parameters["optmessages"] = 0
955 def _post_run(self,_oH=None):
957 if ("StoreSupplementaryCalculations" in self._parameters) and \
958 "APosterioriCovariance" in self._parameters["StoreSupplementaryCalculations"]:
959 for _A in self.StoredVariables["APosterioriCovariance"]:
960 if "APosterioriVariances" in self._parameters["StoreSupplementaryCalculations"]:
961 self.StoredVariables["APosterioriVariances"].store( numpy.diag(_A) )
962 if "APosterioriStandardDeviations" in self._parameters["StoreSupplementaryCalculations"]:
963 self.StoredVariables["APosterioriStandardDeviations"].store( numpy.sqrt(numpy.diag(_A)) )
964 if "APosterioriCorrelations" in self._parameters["StoreSupplementaryCalculations"]:
965 _EI = numpy.diag(1./numpy.sqrt(numpy.diag(_A)))
966 _C = numpy.dot(_EI, numpy.dot(_A, _EI))
967 self.StoredVariables["APosterioriCorrelations"].store( _C )
968 if _oH is not None and "Direct" in _oH and "Tangent" in _oH and "Adjoint" in _oH:
970 "%s Nombre d'évaluation(s) de l'opérateur d'observation direct/tangent/adjoint.: %i/%i/%i",
971 self._name, _oH["Direct"].nbcalls(0),_oH["Tangent"].nbcalls(0),_oH["Adjoint"].nbcalls(0))
973 "%s Nombre d'appels au cache d'opérateur d'observation direct/tangent/adjoint..: %i/%i/%i",
974 self._name, _oH["Direct"].nbcalls(3),_oH["Tangent"].nbcalls(3),_oH["Adjoint"].nbcalls(3))
975 logging.debug("%s Taille mémoire utilisée de %.0f Mio", self._name, self._m.getUsedMemory("Mio"))
976 logging.debug("%s Durées d'utilisation CPU de %.1fs et elapsed de %.1fs", self._name, self._getTimeState()[0], self._getTimeState()[1])
977 logging.debug("%s Terminé", self._name)
980 def _toStore(self, key):
981 "True if in StoreSupplementaryCalculations, else False"
982 return key in self._parameters["StoreSupplementaryCalculations"]
984 def get(self, key=None):
986 Renvoie l'une des variables stockées identifiée par la clé, ou le
987 dictionnaire de l'ensemble des variables disponibles en l'absence de
988 clé. Ce sont directement les variables sous forme objet qui sont
989 renvoyées, donc les méthodes d'accès à l'objet individuel sont celles
990 des classes de persistance.
993 return self.StoredVariables[self.__canonical_stored_name[key.lower()]]
995 return self.StoredVariables
997 def __contains__(self, key=None):
998 "D.__contains__(k) -> True if D has a key k, else False"
999 if key is None or key.lower() not in self.__canonical_stored_name:
1002 return self.__canonical_stored_name[key.lower()] in self.StoredVariables
1005 "D.keys() -> list of D's keys"
1006 if hasattr(self, "StoredVariables"):
1007 return self.StoredVariables.keys()
1011 def pop(self, k, d):
1012 "D.pop(k[,d]) -> v, remove specified key and return the corresponding value"
1013 if hasattr(self, "StoredVariables") and k.lower() in self.__canonical_stored_name:
1014 return self.StoredVariables.pop(self.__canonical_stored_name[k.lower()], d)
1019 raise TypeError("pop expected at least 1 arguments, got 0")
1020 "If key is not found, d is returned if given, otherwise KeyError is raised"
1026 def run(self, Xb=None, Y=None, U=None, HO=None, EM=None, CM=None, R=None, B=None, Q=None, Parameters=None):
1028 Doit implémenter l'opération élémentaire de calcul algorithmique.
1030 raise NotImplementedError("Mathematical algorithmic calculation has not been implemented!")
1032 def defineRequiredParameter(self,
1044 Permet de définir dans l'algorithme des paramètres requis et leurs
1045 caractéristiques par défaut.
1048 raise ValueError("A name is mandatory to define a required parameter.")
1050 self.__required_parameters[name] = {
1051 "default" : default,
1052 "typecast" : typecast,
1055 "listval" : listval,
1056 "listadv" : listadv,
1057 "message" : message,
1058 "oldname" : oldname,
1060 self.__canonical_parameter_name[name.lower()] = name
1061 if oldname is not None:
1062 self.__canonical_parameter_name[oldname.lower()] = name # Conversion
1063 self.__replace_by_the_new_name[oldname.lower()] = name
1064 logging.debug("%s %s (valeur par défaut = %s)", self._name, message, self.setParameterValue(name))
1066 def getRequiredParameters(self, noDetails=True):
1068 Renvoie la liste des noms de paramètres requis ou directement le
1069 dictionnaire des paramètres requis.
1072 return sorted(self.__required_parameters.keys())
1074 return self.__required_parameters
1076 def setParameterValue(self, name=None, value=None):
1078 Renvoie la valeur d'un paramètre requis de manière contrôlée
1080 __k = self.__canonical_parameter_name[name.lower()]
1081 default = self.__required_parameters[__k]["default"]
1082 typecast = self.__required_parameters[__k]["typecast"]
1083 minval = self.__required_parameters[__k]["minval"]
1084 maxval = self.__required_parameters[__k]["maxval"]
1085 listval = self.__required_parameters[__k]["listval"]
1086 listadv = self.__required_parameters[__k]["listadv"]
1088 if value is None and default is None:
1090 elif value is None and default is not None:
1091 if typecast is None: __val = default
1092 else: __val = typecast( default )
1094 if typecast is None: __val = value
1097 __val = typecast( value )
1099 raise ValueError("The value '%s' for the parameter named '%s' can not be correctly evaluated with type '%s'."%(value, __k, typecast))
1101 if minval is not None and (numpy.array(__val, float) < minval).any():
1102 raise ValueError("The parameter named '%s' of value '%s' can not be less than %s."%(__k, __val, minval))
1103 if maxval is not None and (numpy.array(__val, float) > maxval).any():
1104 raise ValueError("The parameter named '%s' of value '%s' can not be greater than %s."%(__k, __val, maxval))
1105 if listval is not None or listadv is not None:
1106 if typecast is list or typecast is tuple or isinstance(__val,list) or isinstance(__val,tuple):
1108 if listval is not None and v in listval: continue
1109 elif listadv is not None and v in listadv: continue
1111 raise ValueError("The value '%s' is not allowed for the parameter named '%s', it has to be in the list %s."%(v, __k, listval))
1112 elif not (listval is not None and __val in listval) and not (listadv is not None and __val in listadv):
1113 raise ValueError("The value '%s' is not allowed for the parameter named '%s', it has to be in the list %s."%( __val, __k,listval))
1115 if __k in ["SetSeed",]:
1120 def requireInputArguments(self, mandatory=(), optional=()):
1122 Permet d'imposer des arguments de calcul requis en entrée.
1124 self.__required_inputs["RequiredInputValues"]["mandatory"] = tuple( mandatory )
1125 self.__required_inputs["RequiredInputValues"]["optional"] = tuple( optional )
1127 def getInputArguments(self):
1129 Permet d'obtenir les listes des arguments de calcul requis en entrée.
1131 return self.__required_inputs["RequiredInputValues"]["mandatory"], self.__required_inputs["RequiredInputValues"]["optional"]
1133 def setAttributes(self, tags=()):
1135 Permet d'adjoindre des attributs comme les tags de classification.
1136 Renvoie la liste actuelle dans tous les cas.
1138 self.__required_inputs["ClassificationTags"].extend( tags )
1139 return self.__required_inputs["ClassificationTags"]
1141 def __setParameters(self, fromDico={}, reset=False):
1143 Permet de stocker les paramètres reçus dans le dictionnaire interne.
1145 self._parameters.update( fromDico )
1146 __inverse_fromDico_keys = {}
1147 for k in fromDico.keys():
1148 if k.lower() in self.__canonical_parameter_name:
1149 __inverse_fromDico_keys[self.__canonical_parameter_name[k.lower()]] = k
1150 #~ __inverse_fromDico_keys = dict([(self.__canonical_parameter_name[k.lower()],k) for k in fromDico.keys()])
1151 __canonic_fromDico_keys = __inverse_fromDico_keys.keys()
1153 for k in __inverse_fromDico_keys.values():
1154 if k.lower() in self.__replace_by_the_new_name:
1155 __newk = self.__replace_by_the_new_name[k.lower()]
1156 __msg = "the parameter \"%s\" used in \"%s\" algorithm case is deprecated and has to be replaced by \"%s\"."%(k,self._name,__newk)
1157 __msg += " Please update your code."
1158 warnings.warn(__msg, FutureWarning, stacklevel=50)
1160 for k in self.__required_parameters.keys():
1161 if k in __canonic_fromDico_keys:
1162 self._parameters[k] = self.setParameterValue(k,fromDico[__inverse_fromDico_keys[k]])
1164 self._parameters[k] = self.setParameterValue(k)
1167 if hasattr(self._parameters[k],"size") and self._parameters[k].size > 100:
1168 logging.debug("%s %s d'une taille totale de %s", self._name, self.__required_parameters[k]["message"], self._parameters[k].size)
1169 elif hasattr(self._parameters[k],"__len__") and len(self._parameters[k]) > 100:
1170 logging.debug("%s %s de longueur %s", self._name, self.__required_parameters[k]["message"], len(self._parameters[k]))
1172 logging.debug("%s %s : %s", self._name, self.__required_parameters[k]["message"], self._parameters[k])
1174 def _setInternalState(self, key=None, value=None, fromDico={}, reset=False):
1176 Permet de stocker des variables nommées constituant l'état interne
1178 if reset: # Vide le dictionnaire préalablement
1179 self.__internal_state = {}
1180 if key is not None and value is not None:
1181 self.__internal_state[key] = value
1182 self.__internal_state.update( dict(fromDico) )
1184 def _getInternalState(self, key=None):
1186 Restitue un état interne sous la forme d'un dictionnaire de variables nommées
1188 if key is not None and key in self.__internal_state:
1189 return self.__internal_state[key]
1191 return self.__internal_state
1193 def _getTimeState(self, reset=False):
1195 Initialise ou restitue le temps de calcul (cpu/elapsed) à la seconde
1198 self.__initial_cpu_time = time.process_time()
1199 self.__initial_elapsed_time = time.perf_counter()
1202 self.__cpu_time = time.process_time() - self.__initial_cpu_time
1203 self.__elapsed_time = time.perf_counter() - self.__initial_elapsed_time
1204 return self.__cpu_time, self.__elapsed_time
1206 def _StopOnTimeLimit(self, X=None, withReason=False):
1207 "Stop criteria on time limit: True/False [+ Reason]"
1208 c, e = self._getTimeState()
1209 if "MaximumCpuTime" in self._parameters and c > self._parameters["MaximumCpuTime"]:
1210 __SC, __SR = True, "Reached maximum CPU time (%.1fs > %.1fs)"%(c, self._parameters["MaximumCpuTime"])
1211 elif "MaximumElapsedTime" in self._parameters and e > self._parameters["MaximumElapsedTime"]:
1212 __SC, __SR = True, "Reached maximum elapsed time (%.1fs > %.1fs)"%(e, self._parameters["MaximumElapsedTime"])
1214 __SC, __SR = False, ""
1220 # ==============================================================================
1221 class PartialAlgorithm(object):
1223 Classe pour mimer "Algorithm" du point de vue stockage, mais sans aucune
1224 action avancée comme la vérification . Pour les méthodes reprises ici,
1225 le fonctionnement est identique à celles de la classe "Algorithm".
1228 "_name", "_parameters", "StoredVariables", "__canonical_stored_name",
1231 def __init__(self, name):
1232 self._name = str( name )
1233 self._parameters = {"StoreSupplementaryCalculations":[]}
1235 self.StoredVariables = {}
1236 self.StoredVariables["Analysis"] = Persistence.OneVector(name = "Analysis")
1237 self.StoredVariables["CostFunctionJ"] = Persistence.OneScalar(name = "CostFunctionJ")
1238 self.StoredVariables["CostFunctionJb"] = Persistence.OneScalar(name = "CostFunctionJb")
1239 self.StoredVariables["CostFunctionJo"] = Persistence.OneScalar(name = "CostFunctionJo")
1240 self.StoredVariables["CurrentIterationNumber"] = Persistence.OneIndex(name = "CurrentIterationNumber")
1241 self.StoredVariables["CurrentStepNumber"] = Persistence.OneIndex(name = "CurrentStepNumber")
1243 self.__canonical_stored_name = {}
1244 for k in self.StoredVariables:
1245 self.__canonical_stored_name[k.lower()] = k
1247 def _toStore(self, key):
1248 "True if in StoreSupplementaryCalculations, else False"
1249 return key in self._parameters["StoreSupplementaryCalculations"]
1251 def get(self, key=None):
1253 Renvoie l'une des variables stockées identifiée par la clé, ou le
1254 dictionnaire de l'ensemble des variables disponibles en l'absence de
1255 clé. Ce sont directement les variables sous forme objet qui sont
1256 renvoyées, donc les méthodes d'accès à l'objet individuel sont celles
1257 des classes de persistance.
1260 return self.StoredVariables[self.__canonical_stored_name[key.lower()]]
1262 return self.StoredVariables
1264 # ==============================================================================
1265 class AlgorithmAndParameters(object):
1267 Classe générale d'interface d'action pour l'algorithme et ses paramètres
1270 "__name", "__algorithm", "__algorithmFile", "__algorithmName", "__A",
1271 "__P", "__Xb", "__Y", "__U", "__HO", "__EM", "__CM", "__B", "__R",
1272 "__Q", "__variable_names_not_public",
1276 name = "GenericAlgorithm",
1283 self.__name = str(name)
1287 self.__algorithm = {}
1288 self.__algorithmFile = None
1289 self.__algorithmName = None
1291 self.updateParameters( asDict, asScript )
1293 if asAlgorithm is None and asScript is not None:
1294 __Algo = Interfaces.ImportFromScript(asScript).getvalue( "Algorithm" )
1296 __Algo = asAlgorithm
1298 if __Algo is not None:
1299 self.__A = str(__Algo)
1300 self.__P.update( {"Algorithm":self.__A} )
1302 self.__setAlgorithm( self.__A )
1304 self.__variable_names_not_public = {"nextStep":False} # Duplication dans Algorithm
1306 def updateParameters(self,
1310 "Mise à jour des paramètres"
1311 if asDict is None and asScript is not None:
1312 __Dict = Interfaces.ImportFromScript(asScript).getvalue( self.__name, "Parameters" )
1316 if __Dict is not None:
1317 self.__P.update( dict(__Dict) )
1319 def executePythonScheme(self, asDictAO = None):
1320 "Permet de lancer le calcul d'assimilation"
1321 Operator.CM.clearCache()
1323 if not isinstance(asDictAO, dict):
1324 raise ValueError("The objects for algorithm calculation have to be given together as a dictionnary, and they are not")
1325 if hasattr(asDictAO["Background"],"getO"): self.__Xb = asDictAO["Background"].getO()
1326 elif hasattr(asDictAO["CheckingPoint"],"getO"): self.__Xb = asDictAO["CheckingPoint"].getO()
1327 else: self.__Xb = None
1328 if hasattr(asDictAO["Observation"],"getO"): self.__Y = asDictAO["Observation"].getO()
1329 else: self.__Y = asDictAO["Observation"]
1330 if hasattr(asDictAO["ControlInput"],"getO"): self.__U = asDictAO["ControlInput"].getO()
1331 else: self.__U = asDictAO["ControlInput"]
1332 if hasattr(asDictAO["ObservationOperator"],"getO"): self.__HO = asDictAO["ObservationOperator"].getO()
1333 else: self.__HO = asDictAO["ObservationOperator"]
1334 if hasattr(asDictAO["EvolutionModel"],"getO"): self.__EM = asDictAO["EvolutionModel"].getO()
1335 else: self.__EM = asDictAO["EvolutionModel"]
1336 if hasattr(asDictAO["ControlModel"],"getO"): self.__CM = asDictAO["ControlModel"].getO()
1337 else: self.__CM = asDictAO["ControlModel"]
1338 self.__B = asDictAO["BackgroundError"]
1339 self.__R = asDictAO["ObservationError"]
1340 self.__Q = asDictAO["EvolutionError"]
1342 self.__shape_validate()
1344 self.__algorithm.run(
1354 Parameters = self.__P,
1358 def executeYACSScheme(self, FileName=None):
1359 "Permet de lancer le calcul d'assimilation"
1360 if FileName is None or not os.path.exists(FileName):
1361 raise ValueError("a YACS file name has to be given for YACS execution.\n")
1363 __file = os.path.abspath(FileName)
1364 logging.debug("The YACS file name is \"%s\"."%__file)
1365 if not PlatformInfo.has_salome or \
1366 not PlatformInfo.has_yacs or \
1367 not PlatformInfo.has_adao:
1368 raise ImportError("\n\n"+\
1369 "Unable to get SALOME, YACS or ADAO environnement variables.\n"+\
1370 "Please load the right environnement before trying to use it.\n")
1373 import SALOMERuntime
1375 SALOMERuntime.RuntimeSALOME_setRuntime()
1377 r = pilot.getRuntime()
1378 xmlLoader = loader.YACSLoader()
1379 xmlLoader.registerProcCataLoader()
1381 catalogAd = r.loadCatalog("proc", __file)
1382 r.addCatalog(catalogAd)
1387 p = xmlLoader.load(__file)
1388 except IOError as ex:
1389 print("The YACS XML schema file can not be loaded: %s"%(ex,))
1391 logger = p.getLogger("parser")
1392 if not logger.isEmpty():
1393 print("The imported YACS XML schema has errors on parsing:")
1394 print(logger.getStr())
1397 print("The YACS XML schema is not valid and will not be executed:")
1398 print(p.getErrorReport())
1400 info=pilot.LinkInfo(pilot.LinkInfo.ALL_DONT_STOP)
1401 p.checkConsistency(info)
1402 if info.areWarningsOrErrors():
1403 print("The YACS XML schema is not coherent and will not be executed:")
1404 print(info.getGlobalRepr())
1406 e = pilot.ExecutorSwig()
1408 if p.getEffectiveState() != pilot.DONE:
1409 print(p.getErrorReport())
1413 def get(self, key = None):
1414 "Vérifie l'existence d'une clé de variable ou de paramètres"
1415 if key in self.__algorithm:
1416 return self.__algorithm.get( key )
1417 elif key in self.__P:
1418 return self.__P[key]
1420 allvariables = self.__P
1421 for k in self.__variable_names_not_public: allvariables.pop(k, None)
1424 def pop(self, k, d):
1425 "Necessaire pour le pickling"
1426 return self.__algorithm.pop(k, d)
1428 def getAlgorithmRequiredParameters(self, noDetails=True):
1429 "Renvoie la liste des paramètres requis selon l'algorithme"
1430 return self.__algorithm.getRequiredParameters(noDetails)
1432 def getAlgorithmInputArguments(self):
1433 "Renvoie la liste des entrées requises selon l'algorithme"
1434 return self.__algorithm.getInputArguments()
1436 def getAlgorithmAttributes(self):
1437 "Renvoie la liste des attributs selon l'algorithme"
1438 return self.__algorithm.setAttributes()
1440 def setObserver(self, __V, __O, __I, __S):
1441 if self.__algorithm is None \
1442 or isinstance(self.__algorithm, dict) \
1443 or not hasattr(self.__algorithm,"StoredVariables"):
1444 raise ValueError("No observer can be build before choosing an algorithm.")
1445 if __V not in self.__algorithm:
1446 raise ValueError("An observer requires to be set on a variable named %s which does not exist."%__V)
1448 self.__algorithm.StoredVariables[ __V ].setDataObserver(
1451 HookParameters = __I,
1454 def removeObserver(self, __V, __O, __A = False):
1455 if self.__algorithm is None \
1456 or isinstance(self.__algorithm, dict) \
1457 or not hasattr(self.__algorithm,"StoredVariables"):
1458 raise ValueError("No observer can be removed before choosing an algorithm.")
1459 if __V not in self.__algorithm:
1460 raise ValueError("An observer requires to be removed on a variable named %s which does not exist."%__V)
1462 return self.__algorithm.StoredVariables[ __V ].removeDataObserver(
1467 def hasObserver(self, __V):
1468 if self.__algorithm is None \
1469 or isinstance(self.__algorithm, dict) \
1470 or not hasattr(self.__algorithm,"StoredVariables"):
1472 if __V not in self.__algorithm:
1474 return self.__algorithm.StoredVariables[ __V ].hasDataObserver()
1477 __allvariables = list(self.__algorithm.keys()) + list(self.__P.keys())
1478 for k in self.__variable_names_not_public:
1479 if k in __allvariables: __allvariables.remove(k)
1480 return __allvariables
1482 def __contains__(self, key=None):
1483 "D.__contains__(k) -> True if D has a key k, else False"
1484 return key in self.__algorithm or key in self.__P
1487 "x.__repr__() <==> repr(x)"
1488 return repr(self.__A)+", "+repr(self.__P)
1491 "x.__str__() <==> str(x)"
1492 return str(self.__A)+", "+str(self.__P)
1494 def __setAlgorithm(self, choice = None ):
1496 Permet de sélectionner l'algorithme à utiliser pour mener à bien l'étude
1497 d'assimilation. L'argument est un champ caractère se rapportant au nom
1498 d'un algorithme réalisant l'opération sur les arguments fixes.
1501 raise ValueError("Error: algorithm choice has to be given")
1502 if self.__algorithmName is not None:
1503 raise ValueError("Error: algorithm choice has already been done as \"%s\", it can't be changed."%self.__algorithmName)
1504 daDirectory = "daAlgorithms"
1506 # Recherche explicitement le fichier complet
1507 # ------------------------------------------
1509 for directory in sys.path:
1510 if os.path.isfile(os.path.join(directory, daDirectory, str(choice)+'.py')):
1511 module_path = os.path.abspath(os.path.join(directory, daDirectory))
1512 if module_path is None:
1514 "No algorithm module named \"%s\" has been found in the search path.\n The search path is %s"%(choice, sys.path))
1516 # Importe le fichier complet comme un module
1517 # ------------------------------------------
1519 sys_path_tmp = sys.path ; sys.path.insert(0,module_path)
1520 self.__algorithmFile = __import__(str(choice), globals(), locals(), [])
1521 if not hasattr(self.__algorithmFile, "ElementaryAlgorithm"):
1522 raise ImportError("this module does not define a valid elementary algorithm.")
1523 self.__algorithmName = str(choice)
1524 sys.path = sys_path_tmp ; del sys_path_tmp
1525 except ImportError as e:
1527 "The module named \"%s\" was found, but is incorrect at the import stage.\n The import error message is: %s"%(choice,e))
1529 # Instancie un objet du type élémentaire du fichier
1530 # -------------------------------------------------
1531 self.__algorithm = self.__algorithmFile.ElementaryAlgorithm()
1534 def __shape_validate(self):
1536 Validation de la correspondance correcte des tailles des variables et
1537 des matrices s'il y en a.
1539 if self.__Xb is None: __Xb_shape = (0,)
1540 elif hasattr(self.__Xb,"size"): __Xb_shape = (self.__Xb.size,)
1541 elif hasattr(self.__Xb,"shape"):
1542 if isinstance(self.__Xb.shape, tuple): __Xb_shape = self.__Xb.shape
1543 else: __Xb_shape = self.__Xb.shape()
1544 else: raise TypeError("The background (Xb) has no attribute of shape: problem !")
1546 if self.__Y is None: __Y_shape = (0,)
1547 elif hasattr(self.__Y,"size"): __Y_shape = (self.__Y.size,)
1548 elif hasattr(self.__Y,"shape"):
1549 if isinstance(self.__Y.shape, tuple): __Y_shape = self.__Y.shape
1550 else: __Y_shape = self.__Y.shape()
1551 else: raise TypeError("The observation (Y) has no attribute of shape: problem !")
1553 if self.__U is None: __U_shape = (0,)
1554 elif hasattr(self.__U,"size"): __U_shape = (self.__U.size,)
1555 elif hasattr(self.__U,"shape"):
1556 if isinstance(self.__U.shape, tuple): __U_shape = self.__U.shape
1557 else: __U_shape = self.__U.shape()
1558 else: raise TypeError("The control (U) has no attribute of shape: problem !")
1560 if self.__B is None: __B_shape = (0,0)
1561 elif hasattr(self.__B,"shape"):
1562 if isinstance(self.__B.shape, tuple): __B_shape = self.__B.shape
1563 else: __B_shape = self.__B.shape()
1564 else: raise TypeError("The a priori errors covariance matrix (B) has no attribute of shape: problem !")
1566 if self.__R is None: __R_shape = (0,0)
1567 elif hasattr(self.__R,"shape"):
1568 if isinstance(self.__R.shape, tuple): __R_shape = self.__R.shape
1569 else: __R_shape = self.__R.shape()
1570 else: raise TypeError("The observation errors covariance matrix (R) has no attribute of shape: problem !")
1572 if self.__Q is None: __Q_shape = (0,0)
1573 elif hasattr(self.__Q,"shape"):
1574 if isinstance(self.__Q.shape, tuple): __Q_shape = self.__Q.shape
1575 else: __Q_shape = self.__Q.shape()
1576 else: raise TypeError("The evolution errors covariance matrix (Q) has no attribute of shape: problem !")
1578 if len(self.__HO) == 0: __HO_shape = (0,0)
1579 elif isinstance(self.__HO, dict): __HO_shape = (0,0)
1580 elif hasattr(self.__HO["Direct"],"shape"):
1581 if isinstance(self.__HO["Direct"].shape, tuple): __HO_shape = self.__HO["Direct"].shape
1582 else: __HO_shape = self.__HO["Direct"].shape()
1583 else: raise TypeError("The observation operator (H) has no attribute of shape: problem !")
1585 if len(self.__EM) == 0: __EM_shape = (0,0)
1586 elif isinstance(self.__EM, dict): __EM_shape = (0,0)
1587 elif hasattr(self.__EM["Direct"],"shape"):
1588 if isinstance(self.__EM["Direct"].shape, tuple): __EM_shape = self.__EM["Direct"].shape
1589 else: __EM_shape = self.__EM["Direct"].shape()
1590 else: raise TypeError("The evolution model (EM) has no attribute of shape: problem !")
1592 if len(self.__CM) == 0: __CM_shape = (0,0)
1593 elif isinstance(self.__CM, dict): __CM_shape = (0,0)
1594 elif hasattr(self.__CM["Direct"],"shape"):
1595 if isinstance(self.__CM["Direct"].shape, tuple): __CM_shape = self.__CM["Direct"].shape
1596 else: __CM_shape = self.__CM["Direct"].shape()
1597 else: raise TypeError("The control model (CM) has no attribute of shape: problem !")
1599 # Vérification des conditions
1600 # ---------------------------
1601 if not( len(__Xb_shape) == 1 or min(__Xb_shape) == 1 ):
1602 raise ValueError("Shape characteristic of background (Xb) is incorrect: \"%s\"."%(__Xb_shape,))
1603 if not( len(__Y_shape) == 1 or min(__Y_shape) == 1 ):
1604 raise ValueError("Shape characteristic of observation (Y) is incorrect: \"%s\"."%(__Y_shape,))
1606 if not( min(__B_shape) == max(__B_shape) ):
1607 raise ValueError("Shape characteristic of a priori errors covariance matrix (B) is incorrect: \"%s\"."%(__B_shape,))
1608 if not( min(__R_shape) == max(__R_shape) ):
1609 raise ValueError("Shape characteristic of observation errors covariance matrix (R) is incorrect: \"%s\"."%(__R_shape,))
1610 if not( min(__Q_shape) == max(__Q_shape) ):
1611 raise ValueError("Shape characteristic of evolution errors covariance matrix (Q) is incorrect: \"%s\"."%(__Q_shape,))
1612 if not( min(__EM_shape) == max(__EM_shape) ):
1613 raise ValueError("Shape characteristic of evolution operator (EM) is incorrect: \"%s\"."%(__EM_shape,))
1615 if len(self.__HO) > 0 and not isinstance(self.__HO, dict) and not( __HO_shape[1] == max(__Xb_shape) ):
1617 "Shape characteristic of observation operator (H)"+\
1618 " \"%s\" and state (X) \"%s\" are incompatible."%(__HO_shape,__Xb_shape))
1619 if len(self.__HO) > 0 and not isinstance(self.__HO, dict) and not( __HO_shape[0] == max(__Y_shape) ):
1621 "Shape characteristic of observation operator (H)"+\
1622 " \"%s\" and observation (Y) \"%s\" are incompatible."%(__HO_shape,__Y_shape))
1623 if len(self.__HO) > 0 and not isinstance(self.__HO, dict) and len(self.__B) > 0 and not( __HO_shape[1] == __B_shape[0] ):
1625 "Shape characteristic of observation operator (H)"+\
1626 " \"%s\" and a priori errors covariance matrix (B) \"%s\" are incompatible."%(__HO_shape,__B_shape))
1627 if len(self.__HO) > 0 and not isinstance(self.__HO, dict) and len(self.__R) > 0 and not( __HO_shape[0] == __R_shape[1] ):
1629 "Shape characteristic of observation operator (H)"+\
1630 " \"%s\" and observation errors covariance matrix (R) \"%s\" are incompatible."%(__HO_shape,__R_shape))
1632 if self.__B is not None and len(self.__B) > 0 and not( __B_shape[1] == max(__Xb_shape) ):
1633 if self.__algorithmName in ["EnsembleBlue",]:
1634 asPersistentVector = self.__Xb.reshape((-1,min(__B_shape)))
1635 self.__Xb = Persistence.OneVector("Background")
1636 for member in asPersistentVector:
1637 self.__Xb.store( numpy.asarray(member, dtype=float) )
1638 __Xb_shape = min(__B_shape)
1641 "Shape characteristic of a priori errors covariance matrix (B)"+\
1642 " \"%s\" and background vector (Xb) \"%s\" are incompatible."%(__B_shape,__Xb_shape))
1644 if self.__R is not None and len(self.__R) > 0 and not( __R_shape[1] == max(__Y_shape) ):
1646 "Shape characteristic of observation errors covariance matrix (R)"+\
1647 " \"%s\" and observation vector (Y) \"%s\" are incompatible."%(__R_shape,__Y_shape))
1649 if self.__EM is not None and len(self.__EM) > 0 and not isinstance(self.__EM, dict) and not( __EM_shape[1] == max(__Xb_shape) ):
1651 "Shape characteristic of evolution model (EM)"+\
1652 " \"%s\" and state (X) \"%s\" are incompatible."%(__EM_shape,__Xb_shape))
1654 if self.__CM is not None and len(self.__CM) > 0 and not isinstance(self.__CM, dict) and not( __CM_shape[1] == max(__U_shape) ):
1656 "Shape characteristic of control model (CM)"+\
1657 " \"%s\" and control (U) \"%s\" are incompatible."%(__CM_shape,__U_shape))
1659 if ("Bounds" in self.__P) \
1660 and (isinstance(self.__P["Bounds"], list) or isinstance(self.__P["Bounds"], tuple)) \
1661 and (len(self.__P["Bounds"]) != max(__Xb_shape)):
1662 raise ValueError("The number \"%s\" of bound pairs for the state (X) components is different of the size \"%s\" of the state itself." \
1663 %(len(self.__P["Bounds"]),max(__Xb_shape)))
1665 if ("StateBoundsForQuantiles" in self.__P) \
1666 and (isinstance(self.__P["StateBoundsForQuantiles"], list) or isinstance(self.__P["StateBoundsForQuantiles"], tuple)) \
1667 and (len(self.__P["StateBoundsForQuantiles"]) != max(__Xb_shape)):
1668 raise ValueError("The number \"%s\" of bound pairs for the quantile state (X) components is different of the size \"%s\" of the state itself." \
1669 %(len(self.__P["StateBoundsForQuantiles"]),max(__Xb_shape)))
1673 # ==============================================================================
1674 class RegulationAndParameters(object):
1676 Classe générale d'interface d'action pour la régulation et ses paramètres
1678 __slots__ = ("__name", "__P")
1681 name = "GenericRegulation",
1688 self.__name = str(name)
1691 if asAlgorithm is None and asScript is not None:
1692 __Algo = Interfaces.ImportFromScript(asScript).getvalue( "Algorithm" )
1694 __Algo = asAlgorithm
1696 if asDict is None and asScript is not None:
1697 __Dict = Interfaces.ImportFromScript(asScript).getvalue( self.__name, "Parameters" )
1701 if __Dict is not None:
1702 self.__P.update( dict(__Dict) )
1704 if __Algo is not None:
1705 self.__P.update( {"Algorithm":str(__Algo)} )
1707 def get(self, key = None):
1708 "Vérifie l'existence d'une clé de variable ou de paramètres"
1710 return self.__P[key]
1714 # ==============================================================================
1715 class DataObserver(object):
1717 Classe générale d'interface de type observer
1719 __slots__ = ("__name", "__V", "__O", "__I")
1722 name = "GenericObserver",
1734 self.__name = str(name)
1739 if onVariable is None:
1740 raise ValueError("setting an observer has to be done over a variable name or a list of variable names, not over None.")
1741 elif type(onVariable) in (tuple, list):
1742 self.__V = tuple(map( str, onVariable ))
1743 if withInfo is None:
1746 self.__I = (str(withInfo),)*len(self.__V)
1747 elif isinstance(onVariable, str):
1748 self.__V = (onVariable,)
1749 if withInfo is None:
1750 self.__I = (onVariable,)
1752 self.__I = (str(withInfo),)
1754 raise ValueError("setting an observer has to be done over a variable name or a list of variable names.")
1756 if asObsObject is not None:
1757 self.__O = asObsObject
1759 __FunctionText = str(UserScript('Observer', asTemplate, asString, asScript))
1760 __Function = Observer2Func(__FunctionText)
1761 self.__O = __Function.getfunc()
1763 for k in range(len(self.__V)):
1766 if ename not in withAlgo:
1767 raise ValueError("An observer is asked to be set on a variable named %s which does not exist."%ename)
1769 withAlgo.setObserver(ename, self.__O, einfo, scheduledBy)
1772 "x.__repr__() <==> repr(x)"
1773 return repr(self.__V)+"\n"+repr(self.__O)
1776 "x.__str__() <==> str(x)"
1777 return str(self.__V)+"\n"+str(self.__O)
1779 # ==============================================================================
1780 class UserScript(object):
1782 Classe générale d'interface de type texte de script utilisateur
1784 __slots__ = ("__name", "__F")
1787 name = "GenericUserScript",
1794 self.__name = str(name)
1796 if asString is not None:
1798 elif self.__name == "UserPostAnalysis" and (asTemplate is not None) and (asTemplate in Templates.UserPostAnalysisTemplates):
1799 self.__F = Templates.UserPostAnalysisTemplates[asTemplate]
1800 elif self.__name == "Observer" and (asTemplate is not None) and (asTemplate in Templates.ObserverTemplates):
1801 self.__F = Templates.ObserverTemplates[asTemplate]
1802 elif asScript is not None:
1803 self.__F = Interfaces.ImportFromScript(asScript).getstring()
1808 "x.__repr__() <==> repr(x)"
1809 return repr(self.__F)
1812 "x.__str__() <==> str(x)"
1813 return str(self.__F)
1815 # ==============================================================================
1816 class ExternalParameters(object):
1818 Classe générale d'interface pour le stockage des paramètres externes
1820 __slots__ = ("__name", "__P")
1823 name = "GenericExternalParameters",
1829 self.__name = str(name)
1832 self.updateParameters( asDict, asScript )
1834 def updateParameters(self,
1838 "Mise à jour des paramètres"
1839 if asDict is None and asScript is not None:
1840 __Dict = Interfaces.ImportFromScript(asScript).getvalue( self.__name, "ExternalParameters" )
1844 if __Dict is not None:
1845 self.__P.update( dict(__Dict) )
1847 def get(self, key = None):
1849 return self.__P[key]
1851 return list(self.__P.keys())
1854 return list(self.__P.keys())
1856 def pop(self, k, d):
1857 return self.__P.pop(k, d)
1860 return self.__P.items()
1862 def __contains__(self, key=None):
1863 "D.__contains__(k) -> True if D has a key k, else False"
1864 return key in self.__P
1866 # ==============================================================================
1867 class State(object):
1869 Classe générale d'interface de type état
1872 "__name", "__check", "__V", "__T", "__is_vector", "__is_series",
1877 name = "GenericVector",
1879 asPersistentVector = None,
1885 toBeChecked = False,
1888 Permet de définir un vecteur :
1889 - asVector : entrée des données, comme un vecteur compatible avec le
1890 constructeur de numpy.matrix, ou "True" si entrée par script.
1891 - asPersistentVector : entrée des données, comme une série de vecteurs
1892 compatible avec le constructeur de numpy.matrix, ou comme un objet de
1893 type Persistence, ou "True" si entrée par script.
1894 - asScript : si un script valide est donné contenant une variable
1895 nommée "name", la variable est de type "asVector" (par défaut) ou
1896 "asPersistentVector" selon que l'une de ces variables est placée à
1898 - asDataFile : si un ou plusieurs fichiers valides sont donnés
1899 contenant des valeurs en colonnes, elles-mêmes nommées "colNames"
1900 (s'il n'y a pas de nom de colonne indiquée, on cherche une colonne
1901 nommée "name"), on récupère les colonnes et on les range ligne après
1902 ligne (colMajor=False, par défaut) ou colonne après colonne
1903 (colMajor=True). La variable résultante est de type "asVector" (par
1904 défaut) ou "asPersistentVector" selon que l'une de ces variables est
1907 self.__name = str(name)
1908 self.__check = bool(toBeChecked)
1912 self.__is_vector = False
1913 self.__is_series = False
1915 if asScript is not None:
1916 __Vector, __Series = None, None
1917 if asPersistentVector:
1918 __Series = Interfaces.ImportFromScript(asScript).getvalue( self.__name )
1920 __Vector = Interfaces.ImportFromScript(asScript).getvalue( self.__name )
1921 elif asDataFile is not None:
1922 __Vector, __Series = None, None
1923 if asPersistentVector:
1924 if colNames is not None:
1925 __Series = Interfaces.ImportFromFile(asDataFile).getvalue( colNames )[1]
1927 __Series = Interfaces.ImportFromFile(asDataFile).getvalue( [self.__name,] )[1]
1928 if bool(colMajor) and not Interfaces.ImportFromFile(asDataFile).getformat() == "application/numpy.npz":
1929 __Series = numpy.transpose(__Series)
1930 elif not bool(colMajor) and Interfaces.ImportFromFile(asDataFile).getformat() == "application/numpy.npz":
1931 __Series = numpy.transpose(__Series)
1933 if colNames is not None:
1934 __Vector = Interfaces.ImportFromFile(asDataFile).getvalue( colNames )[1]
1936 __Vector = Interfaces.ImportFromFile(asDataFile).getvalue( [self.__name,] )[1]
1938 __Vector = numpy.ravel(__Vector, order = "F")
1940 __Vector = numpy.ravel(__Vector, order = "C")
1942 __Vector, __Series = asVector, asPersistentVector
1944 if __Vector is not None:
1945 self.__is_vector = True
1946 if isinstance(__Vector, str):
1947 __Vector = PlatformInfo.strvect2liststr( __Vector )
1948 self.__V = numpy.ravel(numpy.asarray( __Vector, dtype=float )).reshape((-1,1))
1949 self.shape = self.__V.shape
1950 self.size = self.__V.size
1951 elif __Series is not None:
1952 self.__is_series = True
1953 if isinstance(__Series, (tuple, list, numpy.ndarray, numpy.matrix, str)):
1954 self.__V = Persistence.OneVector(self.__name)
1955 if isinstance(__Series, str):
1956 __Series = PlatformInfo.strmatrix2liststr(__Series)
1957 for member in __Series:
1958 if isinstance(member, str):
1959 member = PlatformInfo.strvect2liststr( member )
1960 self.__V.store(numpy.asarray( member, dtype=float ))
1963 if isinstance(self.__V.shape, (tuple, list)):
1964 self.shape = self.__V.shape
1966 self.shape = self.__V.shape()
1967 if len(self.shape) == 1:
1968 self.shape = (self.shape[0],1)
1969 self.size = self.shape[0] * self.shape[1]
1972 "The %s object is improperly defined or undefined,"%self.__name+\
1973 " it requires at minima either a vector, a list/tuple of"+\
1974 " vectors or a persistent object. Please check your vector input.")
1976 if scheduledBy is not None:
1977 self.__T = scheduledBy
1979 def getO(self, withScheduler=False):
1981 return self.__V, self.__T
1982 elif self.__T is None:
1988 "Vérification du type interne"
1989 return self.__is_vector
1992 "Vérification du type interne"
1993 return self.__is_series
1996 "x.__repr__() <==> repr(x)"
1997 return repr(self.__V)
2000 "x.__str__() <==> str(x)"
2001 return str(self.__V)
2003 # ==============================================================================
2004 class Covariance(object):
2006 Classe générale d'interface de type covariance
2009 "__name", "__check", "__C", "__is_scalar", "__is_vector", "__is_matrix",
2010 "__is_object", "shape", "size",
2014 name = "GenericCovariance",
2015 asCovariance = None,
2016 asEyeByScalar = None,
2017 asEyeByVector = None,
2020 toBeChecked = False,
2023 Permet de définir une covariance :
2024 - asCovariance : entrée des données, comme une matrice compatible avec
2025 le constructeur de numpy.matrix
2026 - asEyeByScalar : entrée des données comme un seul scalaire de variance,
2027 multiplicatif d'une matrice de corrélation identité, aucune matrice
2028 n'étant donc explicitement à donner
2029 - asEyeByVector : entrée des données comme un seul vecteur de variance,
2030 à mettre sur la diagonale d'une matrice de corrélation, aucune matrice
2031 n'étant donc explicitement à donner
2032 - asCovObject : entrée des données comme un objet python, qui a les
2033 methodes obligatoires "getT", "getI", "diag", "trace", "__add__",
2034 "__sub__", "__neg__", "__mul__", "__rmul__" et facultatives "shape",
2035 "size", "cholesky", "choleskyI", "asfullmatrix", "__repr__", "__str__"
2036 - toBeChecked : booléen indiquant si le caractère SDP de la matrice
2037 pleine doit être vérifié
2039 self.__name = str(name)
2040 self.__check = bool(toBeChecked)
2043 self.__is_scalar = False
2044 self.__is_vector = False
2045 self.__is_matrix = False
2046 self.__is_object = False
2048 if asScript is not None:
2049 __Matrix, __Scalar, __Vector, __Object = None, None, None, None
2051 __Scalar = Interfaces.ImportFromScript(asScript).getvalue( self.__name )
2053 __Vector = Interfaces.ImportFromScript(asScript).getvalue( self.__name )
2055 __Object = Interfaces.ImportFromScript(asScript).getvalue( self.__name )
2057 __Matrix = Interfaces.ImportFromScript(asScript).getvalue( self.__name )
2059 __Matrix, __Scalar, __Vector, __Object = asCovariance, asEyeByScalar, asEyeByVector, asCovObject
2061 if __Scalar is not None:
2062 if isinstance(__Scalar, str):
2063 __Scalar = PlatformInfo.strvect2liststr( __Scalar )
2064 if len(__Scalar) > 0: __Scalar = __Scalar[0]
2065 if numpy.array(__Scalar).size != 1:
2067 " The diagonal multiplier given to define a sparse matrix is"+\
2068 " not a unique scalar value.\n Its actual measured size is"+\
2069 " %i. Please check your scalar input."%numpy.array(__Scalar).size)
2070 self.__is_scalar = True
2071 self.__C = numpy.abs( float(__Scalar) )
2074 elif __Vector is not None:
2075 if isinstance(__Vector, str):
2076 __Vector = PlatformInfo.strvect2liststr( __Vector )
2077 self.__is_vector = True
2078 self.__C = numpy.abs( numpy.ravel(numpy.asarray( __Vector, dtype=float )) )
2079 self.shape = (self.__C.size,self.__C.size)
2080 self.size = self.__C.size**2
2081 elif __Matrix is not None:
2082 self.__is_matrix = True
2083 self.__C = numpy.matrix( __Matrix, float )
2084 self.shape = self.__C.shape
2085 self.size = self.__C.size
2086 elif __Object is not None:
2087 self.__is_object = True
2089 for at in ("getT","getI","diag","trace","__add__","__sub__","__neg__","__matmul__","__mul__","__rmatmul__","__rmul__"):
2090 if not hasattr(self.__C,at):
2091 raise ValueError("The matrix given for %s as an object has no attribute \"%s\". Please check your object input."%(self.__name,at))
2092 if hasattr(self.__C,"shape"):
2093 self.shape = self.__C.shape
2096 if hasattr(self.__C,"size"):
2097 self.size = self.__C.size
2105 def __validate(self):
2107 if self.__C is None:
2108 raise UnboundLocalError("%s covariance matrix value has not been set!"%(self.__name,))
2109 if self.ismatrix() and min(self.shape) != max(self.shape):
2110 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))
2111 if self.isobject() and min(self.shape) != max(self.shape):
2112 raise ValueError("The matrix given for \"%s\" is not a square one, its shape is %s. Please check your object input."%(self.__name,self.shape))
2113 if self.isscalar() and self.__C <= 0:
2114 raise ValueError("The \"%s\" covariance matrix is not positive-definite. Please check your scalar input %s."%(self.__name,self.__C))
2115 if self.isvector() and (self.__C <= 0).any():
2116 raise ValueError("The \"%s\" covariance matrix is not positive-definite. Please check your vector input."%(self.__name,))
2117 if self.ismatrix() and (self.__check or logging.getLogger().level < logging.WARNING):
2119 numpy.linalg.cholesky( self.__C )
2121 raise ValueError("The %s covariance matrix is not symmetric positive-definite. Please check your matrix input."%(self.__name,))
2122 if self.isobject() and (self.__check or logging.getLogger().level < logging.WARNING):
2126 raise ValueError("The %s covariance object is not symmetric positive-definite. Please check your matrix input."%(self.__name,))
2129 "Vérification du type interne"
2130 return self.__is_scalar
2133 "Vérification du type interne"
2134 return self.__is_vector
2137 "Vérification du type interne"
2138 return self.__is_matrix
2141 "Vérification du type interne"
2142 return self.__is_object
2147 return Covariance(self.__name+"I", asCovariance = numpy.linalg.inv(self.__C) )
2148 elif self.isvector():
2149 return Covariance(self.__name+"I", asEyeByVector = 1. / self.__C )
2150 elif self.isscalar():
2151 return Covariance(self.__name+"I", asEyeByScalar = 1. / self.__C )
2152 elif self.isobject() and hasattr(self.__C,"getI"):
2153 return Covariance(self.__name+"I", asCovObject = self.__C.getI() )
2155 return None # Indispensable
2160 return Covariance(self.__name+"T", asCovariance = self.__C.T )
2161 elif self.isvector():
2162 return Covariance(self.__name+"T", asEyeByVector = self.__C )
2163 elif self.isscalar():
2164 return Covariance(self.__name+"T", asEyeByScalar = self.__C )
2165 elif self.isobject() and hasattr(self.__C,"getT"):
2166 return Covariance(self.__name+"T", asCovObject = self.__C.getT() )
2168 raise AttributeError("the %s covariance matrix has no getT attribute."%(self.__name,))
2171 "Décomposition de Cholesky"
2173 return Covariance(self.__name+"C", asCovariance = numpy.linalg.cholesky(self.__C) )
2174 elif self.isvector():
2175 return Covariance(self.__name+"C", asEyeByVector = numpy.sqrt( self.__C ) )
2176 elif self.isscalar():
2177 return Covariance(self.__name+"C", asEyeByScalar = numpy.sqrt( self.__C ) )
2178 elif self.isobject() and hasattr(self.__C,"cholesky"):
2179 return Covariance(self.__name+"C", asCovObject = self.__C.cholesky() )
2181 raise AttributeError("the %s covariance matrix has no cholesky attribute."%(self.__name,))
2183 def choleskyI(self):
2184 "Inversion de la décomposition de Cholesky"
2186 return Covariance(self.__name+"H", asCovariance = numpy.linalg.inv(numpy.linalg.cholesky(self.__C)) )
2187 elif self.isvector():
2188 return Covariance(self.__name+"H", asEyeByVector = 1.0 / numpy.sqrt( self.__C ) )
2189 elif self.isscalar():
2190 return Covariance(self.__name+"H", asEyeByScalar = 1.0 / numpy.sqrt( self.__C ) )
2191 elif self.isobject() and hasattr(self.__C,"choleskyI"):
2192 return Covariance(self.__name+"H", asCovObject = self.__C.choleskyI() )
2194 raise AttributeError("the %s covariance matrix has no choleskyI attribute."%(self.__name,))
2197 "Racine carrée matricielle"
2200 return Covariance(self.__name+"C", asCovariance = numpy.real(scipy.linalg.sqrtm(self.__C)) )
2201 elif self.isvector():
2202 return Covariance(self.__name+"C", asEyeByVector = numpy.sqrt( self.__C ) )
2203 elif self.isscalar():
2204 return Covariance(self.__name+"C", asEyeByScalar = numpy.sqrt( self.__C ) )
2205 elif self.isobject() and hasattr(self.__C,"sqrtm"):
2206 return Covariance(self.__name+"C", asCovObject = self.__C.sqrtm() )
2208 raise AttributeError("the %s covariance matrix has no sqrtm attribute."%(self.__name,))
2211 "Inversion de la racine carrée matricielle"
2214 return Covariance(self.__name+"H", asCovariance = numpy.linalg.inv(numpy.real(scipy.linalg.sqrtm(self.__C))) )
2215 elif self.isvector():
2216 return Covariance(self.__name+"H", asEyeByVector = 1.0 / numpy.sqrt( self.__C ) )
2217 elif self.isscalar():
2218 return Covariance(self.__name+"H", asEyeByScalar = 1.0 / numpy.sqrt( self.__C ) )
2219 elif self.isobject() and hasattr(self.__C,"sqrtmI"):
2220 return Covariance(self.__name+"H", asCovObject = self.__C.sqrtmI() )
2222 raise AttributeError("the %s covariance matrix has no sqrtmI attribute."%(self.__name,))
2224 def diag(self, msize=None):
2225 "Diagonale de la matrice"
2227 return numpy.diag(self.__C)
2228 elif self.isvector():
2230 elif self.isscalar():
2232 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,))
2234 return self.__C * numpy.ones(int(msize))
2235 elif self.isobject() and hasattr(self.__C,"diag"):
2236 return self.__C.diag()
2238 raise AttributeError("the %s covariance matrix has no diag attribute."%(self.__name,))
2240 def trace(self, msize=None):
2241 "Trace de la matrice"
2243 return numpy.trace(self.__C)
2244 elif self.isvector():
2245 return float(numpy.sum(self.__C))
2246 elif self.isscalar():
2248 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,))
2250 return self.__C * int(msize)
2251 elif self.isobject():
2252 return self.__C.trace()
2254 raise AttributeError("the %s covariance matrix has no trace attribute."%(self.__name,))
2256 def asfullmatrix(self, msize=None):
2259 return numpy.asarray(self.__C, dtype=float)
2260 elif self.isvector():
2261 return numpy.asarray( numpy.diag(self.__C), dtype=float )
2262 elif self.isscalar():
2264 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,))
2266 return numpy.asarray( self.__C * numpy.eye(int(msize)), dtype=float )
2267 elif self.isobject() and hasattr(self.__C,"asfullmatrix"):
2268 return self.__C.asfullmatrix()
2270 raise AttributeError("the %s covariance matrix has no asfullmatrix attribute."%(self.__name,))
2272 def assparsematrix(self):
2280 "x.__repr__() <==> repr(x)"
2281 return repr(self.__C)
2284 "x.__str__() <==> str(x)"
2285 return str(self.__C)
2287 def __add__(self, other):
2288 "x.__add__(y) <==> x+y"
2289 if self.ismatrix() or self.isobject():
2290 return self.__C + numpy.asmatrix(other)
2291 elif self.isvector() or self.isscalar():
2292 _A = numpy.asarray(other)
2293 if len(_A.shape) == 1:
2294 _A.reshape((-1,1))[::2] += self.__C
2296 _A.reshape(_A.size)[::_A.shape[1]+1] += self.__C
2297 return numpy.asmatrix(_A)
2299 def __radd__(self, other):
2300 "x.__radd__(y) <==> y+x"
2301 raise NotImplementedError("%s covariance matrix __radd__ method not available for %s type!"%(self.__name,type(other)))
2303 def __sub__(self, other):
2304 "x.__sub__(y) <==> x-y"
2305 if self.ismatrix() or self.isobject():
2306 return self.__C - numpy.asmatrix(other)
2307 elif self.isvector() or self.isscalar():
2308 _A = numpy.asarray(other)
2309 _A.reshape(_A.size)[::_A.shape[1]+1] = self.__C - _A.reshape(_A.size)[::_A.shape[1]+1]
2310 return numpy.asmatrix(_A)
2312 def __rsub__(self, other):
2313 "x.__rsub__(y) <==> y-x"
2314 raise NotImplementedError("%s covariance matrix __rsub__ method not available for %s type!"%(self.__name,type(other)))
2317 "x.__neg__() <==> -x"
2320 def __matmul__(self, other):
2321 "x.__mul__(y) <==> x@y"
2322 if self.ismatrix() and isinstance(other, (int, float)):
2323 return numpy.asarray(self.__C) * other
2324 elif self.ismatrix() and isinstance(other, (list, numpy.matrix, numpy.ndarray, tuple)):
2325 if numpy.ravel(other).size == self.shape[1]: # Vecteur
2326 return numpy.ravel(self.__C @ numpy.ravel(other))
2327 elif numpy.asarray(other).shape[0] == self.shape[1]: # Matrice
2328 return numpy.asarray(self.__C) @ numpy.asarray(other)
2330 raise ValueError("operands could not be broadcast together with shapes %s %s in %s matrix"%(self.shape,numpy.asarray(other).shape,self.__name))
2331 elif self.isvector() and isinstance(other, (list, numpy.matrix, numpy.ndarray, tuple)):
2332 if numpy.ravel(other).size == self.shape[1]: # Vecteur
2333 return numpy.ravel(self.__C) * numpy.ravel(other)
2334 elif numpy.asarray(other).shape[0] == self.shape[1]: # Matrice
2335 return numpy.ravel(self.__C).reshape((-1,1)) * numpy.asarray(other)
2337 raise ValueError("operands could not be broadcast together with shapes %s %s in %s matrix"%(self.shape,numpy.ravel(other).shape,self.__name))
2338 elif self.isscalar() and isinstance(other,numpy.matrix):
2339 return numpy.asarray(self.__C * other)
2340 elif self.isscalar() and isinstance(other, (list, numpy.ndarray, tuple)):
2341 if len(numpy.asarray(other).shape) == 1 or numpy.asarray(other).shape[1] == 1 or numpy.asarray(other).shape[0] == 1:
2342 return self.__C * numpy.ravel(other)
2344 return self.__C * numpy.asarray(other)
2345 elif self.isobject():
2346 return self.__C.__matmul__(other)
2348 raise NotImplementedError("%s covariance matrix __matmul__ method not available for %s type!"%(self.__name,type(other)))
2350 def __mul__(self, other):
2351 "x.__mul__(y) <==> x*y"
2352 if self.ismatrix() and isinstance(other, (int, numpy.matrix, float)):
2353 return self.__C * other
2354 elif self.ismatrix() and isinstance(other, (list, numpy.ndarray, tuple)):
2355 if numpy.ravel(other).size == self.shape[1]: # Vecteur
2356 return self.__C * numpy.asmatrix(numpy.ravel(other)).T
2357 elif numpy.asmatrix(other).shape[0] == self.shape[1]: # Matrice
2358 return self.__C * numpy.asmatrix(other)
2361 "operands could not be broadcast together with shapes %s %s in %s matrix"%(self.shape,numpy.asmatrix(other).shape,self.__name))
2362 elif self.isvector() and isinstance(other, (list, numpy.matrix, numpy.ndarray, tuple)):
2363 if numpy.ravel(other).size == self.shape[1]: # Vecteur
2364 return numpy.asmatrix(self.__C * numpy.ravel(other)).T
2365 elif numpy.asmatrix(other).shape[0] == self.shape[1]: # Matrice
2366 return numpy.asmatrix((self.__C * (numpy.asarray(other).transpose())).transpose())
2369 "operands could not be broadcast together with shapes %s %s in %s matrix"%(self.shape,numpy.ravel(other).shape,self.__name))
2370 elif self.isscalar() and isinstance(other,numpy.matrix):
2371 return self.__C * other
2372 elif self.isscalar() and isinstance(other, (list, numpy.ndarray, tuple)):
2373 if len(numpy.asarray(other).shape) == 1 or numpy.asarray(other).shape[1] == 1 or numpy.asarray(other).shape[0] == 1:
2374 return self.__C * numpy.asmatrix(numpy.ravel(other)).T
2376 return self.__C * numpy.asmatrix(other)
2377 elif self.isobject():
2378 return self.__C.__mul__(other)
2380 raise NotImplementedError(
2381 "%s covariance matrix __mul__ method not available for %s type!"%(self.__name,type(other)))
2383 def __rmatmul__(self, other):
2384 "x.__rmul__(y) <==> y@x"
2385 if self.ismatrix() and isinstance(other, (int, numpy.matrix, float)):
2386 return other * self.__C
2387 elif self.ismatrix() and isinstance(other, (list, numpy.ndarray, tuple)):
2388 if numpy.ravel(other).size == self.shape[1]: # Vecteur
2389 return numpy.asmatrix(numpy.ravel(other)) * self.__C
2390 elif numpy.asmatrix(other).shape[0] == self.shape[1]: # Matrice
2391 return numpy.asmatrix(other) * self.__C
2394 "operands could not be broadcast together with shapes %s %s in %s matrix"%(numpy.asmatrix(other).shape,self.shape,self.__name))
2395 elif self.isvector() and isinstance(other,numpy.matrix):
2396 if numpy.ravel(other).size == self.shape[0]: # Vecteur
2397 return numpy.asmatrix(numpy.ravel(other) * self.__C)
2398 elif numpy.asmatrix(other).shape[1] == self.shape[0]: # Matrice
2399 return numpy.asmatrix(numpy.array(other) * self.__C)
2402 "operands could not be broadcast together with shapes %s %s in %s matrix"%(numpy.ravel(other).shape,self.shape,self.__name))
2403 elif self.isscalar() and isinstance(other,numpy.matrix):
2404 return other * self.__C
2405 elif self.isobject():
2406 return self.__C.__rmatmul__(other)
2408 raise NotImplementedError(
2409 "%s covariance matrix __rmatmul__ method not available for %s type!"%(self.__name,type(other)))
2411 def __rmul__(self, other):
2412 "x.__rmul__(y) <==> y*x"
2413 if self.ismatrix() and isinstance(other, (int, numpy.matrix, float)):
2414 return other * self.__C
2415 elif self.ismatrix() and isinstance(other, (list, numpy.ndarray, tuple)):
2416 if numpy.ravel(other).size == self.shape[1]: # Vecteur
2417 return numpy.asmatrix(numpy.ravel(other)) * self.__C
2418 elif numpy.asmatrix(other).shape[0] == self.shape[1]: # Matrice
2419 return numpy.asmatrix(other) * self.__C
2422 "operands could not be broadcast together with shapes %s %s in %s matrix"%(numpy.asmatrix(other).shape,self.shape,self.__name))
2423 elif self.isvector() and isinstance(other,numpy.matrix):
2424 if numpy.ravel(other).size == self.shape[0]: # Vecteur
2425 return numpy.asmatrix(numpy.ravel(other) * self.__C)
2426 elif numpy.asmatrix(other).shape[1] == self.shape[0]: # Matrice
2427 return numpy.asmatrix(numpy.array(other) * self.__C)
2430 "operands could not be broadcast together with shapes %s %s in %s matrix"%(numpy.ravel(other).shape,self.shape,self.__name))
2431 elif self.isscalar() and isinstance(other,numpy.matrix):
2432 return other * self.__C
2433 elif self.isscalar() and isinstance(other,float):
2434 return other * self.__C
2435 elif self.isobject():
2436 return self.__C.__rmul__(other)
2438 raise NotImplementedError(
2439 "%s covariance matrix __rmul__ method not available for %s type!"%(self.__name,type(other)))
2442 "x.__len__() <==> len(x)"
2443 return self.shape[0]
2445 # ==============================================================================
2446 class Observer2Func(object):
2448 Création d'une fonction d'observateur a partir de son texte
2450 __slots__ = ("__corps")
2452 def __init__(self, corps=""):
2453 self.__corps = corps
2454 def func(self,var,info):
2455 "Fonction d'observation"
2458 "Restitution du pointeur de fonction dans l'objet"
2461 # ==============================================================================
2462 class CaseLogger(object):
2464 Conservation des commandes de création d'un cas
2467 "__name", "__objname", "__logSerie", "__switchoff", "__viewers",
2471 def __init__(self, __name="", __objname="case", __addViewers=None, __addLoaders=None):
2472 self.__name = str(__name)
2473 self.__objname = str(__objname)
2474 self.__logSerie = []
2475 self.__switchoff = False
2477 "TUI" :Interfaces._TUIViewer,
2478 "SCD" :Interfaces._SCDViewer,
2479 "YACS":Interfaces._YACSViewer,
2480 "SimpleReportInRst":Interfaces._SimpleReportInRstViewer,
2481 "SimpleReportInHtml":Interfaces._SimpleReportInHtmlViewer,
2482 "SimpleReportInPlainTxt":Interfaces._SimpleReportInPlainTxtViewer,
2485 "TUI" :Interfaces._TUIViewer,
2486 "COM" :Interfaces._COMViewer,
2488 if __addViewers is not None:
2489 self.__viewers.update(dict(__addViewers))
2490 if __addLoaders is not None:
2491 self.__loaders.update(dict(__addLoaders))
2493 def register(self, __command=None, __keys=None, __local=None, __pre=None, __switchoff=False):
2494 "Enregistrement d'une commande individuelle"
2495 if __command is not None and __keys is not None and __local is not None and not self.__switchoff:
2496 if "self" in __keys: __keys.remove("self")
2497 self.__logSerie.append( (str(__command), __keys, __local, __pre, __switchoff) )
2499 self.__switchoff = True
2501 self.__switchoff = False
2503 def dump(self, __filename=None, __format="TUI", __upa=""):
2504 "Restitution normalisée des commandes"
2505 if __format in self.__viewers:
2506 __formater = self.__viewers[__format](self.__name, self.__objname, self.__logSerie)
2508 raise ValueError("Dumping as \"%s\" is not available"%__format)
2509 return __formater.dump(__filename, __upa)
2511 def load(self, __filename=None, __content=None, __object=None, __format="TUI"):
2512 "Chargement normalisé des commandes"
2513 if __format in self.__loaders:
2514 __formater = self.__loaders[__format]()
2516 raise ValueError("Loading as \"%s\" is not available"%__format)
2517 return __formater.load(__filename, __content, __object)
2519 # ==============================================================================
2522 _extraArguments = None,
2523 _sFunction = lambda x: x,
2528 Pour une liste ordonnée de vecteurs en entrée, renvoie en sortie la liste
2529 correspondante de valeurs de la fonction en argument
2531 # Vérifications et définitions initiales
2532 # logging.debug("MULTF Internal multifonction calculations begin with function %s"%(_sFunction.__name__,))
2533 if not PlatformInfo.isIterable( __xserie ):
2534 raise TypeError("MultiFonction not iterable unkown input type: %s"%(type(__xserie),))
2536 if (_mpWorkers is None) or (_mpWorkers is not None and _mpWorkers < 1):
2539 __mpWorkers = int(_mpWorkers)
2541 import multiprocessing
2552 # logging.debug("MULTF Internal multiprocessing calculations begin : evaluation of %i point(s)"%(len(_jobs),))
2553 with multiprocessing.Pool(__mpWorkers) as pool:
2554 __multiHX = pool.map( _sFunction, _jobs )
2557 # logging.debug("MULTF Internal multiprocessing calculation end")
2559 # logging.debug("MULTF Internal monoprocessing calculation begin")
2561 if _extraArguments is None:
2562 for __xvalue in __xserie:
2563 __multiHX.append( _sFunction( __xvalue ) )
2564 elif _extraArguments is not None and isinstance(_extraArguments, (list, tuple, map)):
2565 for __xvalue in __xserie:
2566 __multiHX.append( _sFunction( __xvalue, *_extraArguments ) )
2567 elif _extraArguments is not None and isinstance(_extraArguments, dict):
2568 for __xvalue in __xserie:
2569 __multiHX.append( _sFunction( __xvalue, **_extraArguments ) )
2571 raise TypeError("MultiFonction extra arguments unkown input type: %s"%(type(_extraArguments),))
2572 # logging.debug("MULTF Internal monoprocessing calculation end")
2574 # logging.debug("MULTF Internal multifonction calculations end")
2577 # ==============================================================================
2578 if __name__ == "__main__":
2579 print("\n AUTODIAGNOSTIC\n")