From 3a122e5d69f1067c05beb887ed4da94a959f0e9d Mon Sep 17 00:00:00 2001 From: Jean-Philippe ARGAUD Date: Mon, 28 Jul 2014 20:51:49 +0200 Subject: [PATCH] Improving code quality --- src/daComposant/daAlgorithms/FunctionTest.py | 1 + .../daAlgorithms/ParticleSwarmOptimization.py | 12 +++- src/daComposant/daCore/AssimilationStudy.py | 62 ++++++++++--------- src/daComposant/daCore/BasicObjects.py | 54 ++++++++-------- src/daComposant/daCore/ExtendedLogging.py | 12 ++-- 5 files changed, 77 insertions(+), 64 deletions(-) diff --git a/src/daComposant/daAlgorithms/FunctionTest.py b/src/daComposant/daAlgorithms/FunctionTest.py index e656b5c..ee983c2 100644 --- a/src/daComposant/daAlgorithms/FunctionTest.py +++ b/src/daComposant/daAlgorithms/FunctionTest.py @@ -117,6 +117,7 @@ class ElementaryAlgorithm(BasicObjects.Algorithm): Ys.append( copy.copy( numpy.ravel( Yn ) ) ) + # ---------- # print(" %s\n"%("-"*75,)) if self._parameters["SetDebug"]: diff --git a/src/daComposant/daAlgorithms/ParticleSwarmOptimization.py b/src/daComposant/daAlgorithms/ParticleSwarmOptimization.py index ba52587..113281e 100644 --- a/src/daComposant/daAlgorithms/ParticleSwarmOptimization.py +++ b/src/daComposant/daAlgorithms/ParticleSwarmOptimization.py @@ -147,9 +147,9 @@ class ElementaryAlgorithm(BasicObjects.Algorithm): # if self._parameters["StoreInternalVariables"]: self.StoredVariables["CurrentState"].store( _X ) - self.StoredVariables["CostFunctionJb"].store( Jb ) - self.StoredVariables["CostFunctionJo"].store( Jo ) - self.StoredVariables["CostFunctionJ" ].store( J ) + self.StoredVariables["CostFunctionJb"].store( Jb ) + self.StoredVariables["CostFunctionJo"].store( Jo ) + self.StoredVariables["CostFunctionJ" ].store( J ) return J # # Point de démarrage de l'optimisation : Xini = Xb @@ -193,6 +193,12 @@ class ElementaryAlgorithm(BasicObjects.Algorithm): qBest = copy.copy( quality ) logging.debug("%s Initialisation, Insecte = %s, Qualité = %s"%(self._name, str(Best), str(qBest))) # + if self._parameters["StoreInternalVariables"]: + self.StoredVariables["CurrentState"].store( Best ) + self.StoredVariables["CostFunctionJb"].store( 0. ) + self.StoredVariables["CostFunctionJo"].store( 0. ) + self.StoredVariables["CostFunctionJ" ].store( qBest ) + # # Minimisation de la fonctionnelle # -------------------------------- for n in range(self._parameters["MaximumNumberOfSteps"]): diff --git a/src/daComposant/daCore/AssimilationStudy.py b/src/daComposant/daCore/AssimilationStudy.py index b9010d8..d06f402 100644 --- a/src/daComposant/daCore/AssimilationStudy.py +++ b/src/daComposant/daCore/AssimilationStudy.py @@ -20,10 +20,10 @@ # # Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D -__doc__ = """ +""" Classe principale pour la préparation, la réalisation et la restitution de calculs d'assimilation de données. - + Ce module est destiné à être appelé par AssimilationStudy pour constituer les objets élémentaires de l'étude. """ @@ -32,10 +32,12 @@ __author__ = "Jean-Philippe ARGAUD" import os, sys import numpy import ExtendedLogging ; ExtendedLogging.ExtendedLogging() # A importer en premier +import logging try: import scipy.optimize -except: - pass + logging.debug("Succeed initial import of scipy.optimize with Scipy %s", scipy.version.version) +except ImportError: + logging.debug("Fail initial import of scipy.optimize") import Persistence from BasicObjects import Operator, Covariance from PlatformInfo import uniq @@ -128,9 +130,9 @@ class AssimilationStudy: else: raise ValueError("Error: improperly defined background, it requires at minima either a vector, a list/tuple of vectors or a persistent object") if toBeStored: - self.__StoredInputs["Background"] = self.__Xb + self.__StoredInputs["Background"] = self.__Xb return 0 - + def setBackgroundError(self, asCovariance = None, asEyeByScalar = None, @@ -618,7 +620,7 @@ class AssimilationStudy: # self.__StoredInputs["AlgorithmParameters"] = self.__Parameters return 0 - + def getAlgorithmParameters(self, noDetails=True): """ Renvoie la liste des paramètres requis selon l'algorithme @@ -778,9 +780,10 @@ class AssimilationStudy: # if self.__StoredInputs.has_key("AlgorithmParameters") \ and self.__StoredInputs["AlgorithmParameters"].has_key("Bounds") \ - and (type(self.__StoredInputs["AlgorithmParameters"]["Bounds"]) is type([]) or type(self._parameters["Bounds"]) is type(())) \ + and (type(self.__StoredInputs["AlgorithmParameters"]["Bounds"]) is type([]) or type(self.__StoredInputs["AlgorithmParameters"]["Bounds"]) is type(())) \ and (len(self.__StoredInputs["AlgorithmParameters"]["Bounds"]) != max(__Xb_shape)): - raise ValueError("The number \"%s\" of bound pairs for the state (X) components is different of the size \"%s\" of the state itself."%(len(self.__StoredInputs["AlgorithmParameters"]["Bounds"]),max(__Xb_shape))) + raise ValueError("The number \"%s\" of bound pairs for the state (X) components is different of the size \"%s\" of the state itself." \ + %(len(self.__StoredInputs["AlgorithmParameters"]["Bounds"]),max(__Xb_shape))) # return 1 @@ -788,7 +791,7 @@ class AssimilationStudy: def analyze(self): """ Permet de lancer le calcul d'assimilation. - + Le nom de la méthode à activer est toujours "run". Les paramètres en arguments de la méthode sont fixés. En sortie, on obtient les résultats dans la variable de type dictionnaire "StoredVariables", qui contient en @@ -833,7 +836,7 @@ class AssimilationStudy: allvariables.update( self.__StoredDiagnostics ) allvariables.update( self.__StoredInputs ) return allvariables - + def get_available_variables(self): """ Renvoie les variables potentiellement utilisables pour l'étude, @@ -851,7 +854,7 @@ class AssimilationStudy: variables.extend( self.__StoredInputs.keys() ) variables.sort() return variables - + def get_available_algorithms(self): """ Renvoie la liste des algorithmes potentiellement utilisables, identifiés @@ -866,7 +869,7 @@ class AssimilationStudy: files.append(root) files.sort() return files - + def get_available_diagnostics(self): """ Renvoie la liste des diagnostics potentiellement utilisables, identifiés @@ -894,7 +897,7 @@ class AssimilationStudy: """ Ajoute au chemin de recherche des algorithmes un répertoire dans lequel se trouve un sous-répertoire "daAlgorithms" - + Remarque : si le chemin a déjà été ajouté pour les diagnostics, il n'est pas indispensable de le rajouter ici. """ @@ -919,7 +922,7 @@ class AssimilationStudy: """ Ajoute au chemin de recherche des algorithmes un répertoire dans lequel se trouve un sous-répertoire "daDiagnostics" - + Remarque : si le chemin a déjà été ajouté pour les algorithmes, il n'est pas indispensable de le rajouter ici. """ @@ -946,7 +949,7 @@ class AssimilationStudy: pas demandé dans le Scheduler, il effectue la fonction HookFunction avec les arguments (variable persistante VariableName, paramètres HookParameters). """ - # + # if type( self.__algorithm ) is dict: raise ValueError("No observer can be build before choosing an algorithm.") # @@ -964,12 +967,12 @@ class AssimilationStudy: for n in VariableNames: if not self.__algorithm.has_key( n ): raise ValueError("An observer requires to be set on a variable named %s which does not exist."%n) - else: - self.__algorithm.StoredVariables[ n ].setDataObserver( - Scheduler = Scheduler, - HookFunction = HookFunction, - HookParameters = HookParameters, - ) + else: + self.__algorithm.StoredVariables[ n ].setDataObserver( + Scheduler = Scheduler, + HookFunction = HookFunction, + HookParameters = HookParameters, + ) def removeDataObserver(self, VariableName = None, @@ -978,7 +981,7 @@ class AssimilationStudy: """ Permet de retirer un observer à une ou des variable nommée. """ - # + # if type( self.__algorithm ) is dict: raise ValueError("No observer can be removed before choosing an algorithm.") # @@ -996,10 +999,10 @@ class AssimilationStudy: for n in VariableNames: if not self.__algorithm.has_key( n ): raise ValueError("An observer requires to be removed on a variable named %s which does not exist."%n) - else: - self.__algorithm.StoredVariables[ n ].removeDataObserver( - HookFunction = HookFunction, - ) + else: + self.__algorithm.StoredVariables[ n ].removeDataObserver( + HookFunction = HookFunction, + ) # ----------------------------------------------------------- def setDebug(self, level=10): @@ -1008,7 +1011,6 @@ class AssimilationStudy: appel pour changer le niveau de verbosité, avec : NOTSET=0 < DEBUG=10 < INFO=20 < WARNING=30 < ERROR=40 < CRITICAL=50 """ - import logging log = logging.getLogger() log.setLevel( level ) @@ -1016,11 +1018,13 @@ class AssimilationStudy: """ Remet le logger au niveau par défaut """ - import logging log = logging.getLogger() log.setLevel( logging.WARNING ) def prepare_to_pickle(self): + """ + Retire les variables non pickelisables + """ self.__algorithmFile = None self.__diagnosticFile = None self.__HO = {} diff --git a/src/daComposant/daCore/BasicObjects.py b/src/daComposant/daCore/BasicObjects.py index 6051264..608be79 100644 --- a/src/daComposant/daCore/BasicObjects.py +++ b/src/daComposant/daCore/BasicObjects.py @@ -20,9 +20,9 @@ # # Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D -__doc__ = """ +""" Définit les outils généraux élémentaires. - + Ce module est destiné à etre appelée par AssimilationStudy pour constituer les objets élémentaires de l'algorithme. """ @@ -43,7 +43,7 @@ class CacheManager: lenghtOfRedundancy = -1, ): """ - + Les caractéristiques de tolérance peuvent être modifées à la création. """ self.__tolerBP = float(toleranceInRedundancy) self.__lenghtOR = int(lenghtOfRedundancy) @@ -53,7 +53,7 @@ class CacheManager: self.__listOPCV = [] # Operator Previous Calculated Points, Results, Point Norms # logging.debug("CM Tolerance de determination des doublons : %.2e"%self.__tolerBP) - def wasCalculatedIn(self, xValue, info="" ): + def wasCalculatedIn(self, xValue ): #, info="" ): __alc = False __HxV = None for i in xrange(min(len(self.__listOPCV),self.__lenghtOR)-1,-1,-1): @@ -238,11 +238,11 @@ class Operator: class Algorithm: """ Classe générale d'interface de type algorithme - + Elle donne un cadre pour l'écriture d'une classe élémentaire d'algorithme d'assimilation, en fournissant un container (dictionnaire) de variables persistantes initialisées, et des méthodes d'accès à ces variables stockées. - + Une classe élémentaire d'algorithme doit implémenter la méthode "run". """ def __init__(self, name): @@ -251,7 +251,7 @@ class Algorithm: disponibles de manière générique dans les algorithmes élémentaires. Ces variables de stockage sont ensuite conservées dans un dictionnaire interne à l'objet, mais auquel on accède par la méthode "get". - + Les variables prévues sont : - CostFunctionJ : fonction-cout globale, somme des deux parties suivantes - CostFunctionJb : partie ébauche ou background de la fonction-cout @@ -260,8 +260,9 @@ class Algorithm: - GradientOfCostFunctionJb : gradient de la partie ébauche de la fonction-cout - GradientOfCostFunctionJo : gradient de la partie observations de la fonction-cout - CurrentState : état courant lors d'itérations - - Analysis : l'analyse - - Innovation : l'innovation : d = Y - H Xb + - Analysis : l'analyse Xa + - ObservedState : l'état observé H(X) + - Innovation : l'innovation : d = Y - H(X) - SigmaObs2 : indicateur de correction optimale des erreurs d'observation - SigmaBck2 : indicateur de correction optimale des erreurs d'ébauche - MahalanobisConsistency : indicateur de consistance des covariances @@ -288,6 +289,7 @@ class Algorithm: self.StoredVariables["GradientOfCostFunctionJo"] = Persistence.OneVector(name = "GradientOfCostFunctionJo") self.StoredVariables["CurrentState"] = Persistence.OneVector(name = "CurrentState") self.StoredVariables["Analysis"] = Persistence.OneVector(name = "Analysis") + self.StoredVariables["ObservedState"] = Persistence.OneVector(name = "ObservedState") self.StoredVariables["Innovation"] = Persistence.OneVector(name = "Innovation") self.StoredVariables["SigmaObs2"] = Persistence.OneScalar(name = "SigmaObs2") self.StoredVariables["SigmaBck2"] = Persistence.OneScalar(name = "SigmaBck2") @@ -421,11 +423,11 @@ class Algorithm: class Diagnostic: """ Classe générale d'interface de type diagnostic - + Ce template s'utilise de la manière suivante : il sert de classe "patron" en même temps que l'une des classes de persistance, comme "OneScalar" par exemple. - + Une classe élémentaire de diagnostic doit implémenter ses deux méthodes, la méthode "_formula" pour écrire explicitement et proprement la formule pour l'écriture mathématique du calcul du diagnostic (méthode interne non @@ -498,7 +500,7 @@ class Covariance: # raise ValueError("The %s covariance matrix has to be specified either as a matrix, a vector for its diagonal or a scalar multiplying an identity matrix."%self.__name) # self.__validate() - + def __validate(self): if self.ismatrix() and min(self.shape) != max(self.shape): 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)) @@ -511,16 +513,16 @@ class Covariance: L = numpy.linalg.cholesky( self.__B ) except: raise ValueError("The %s covariance matrix is not symmetric positive-definite. Please check your matrix input."%(self.__name,)) - + def isscalar(self): return self.__is_scalar - + def isvector(self): return self.__is_vector - + def ismatrix(self): return self.__is_matrix - + def getI(self): if self.ismatrix(): return Covariance(self.__name+"I", asCovariance = self.__B.I ) @@ -530,7 +532,7 @@ class Covariance: return Covariance(self.__name+"I", asEyeByScalar = 1. / self.__B ) else: return None - + def getT(self): if self.ismatrix(): return Covariance(self.__name+"T", asCovariance = self.__B.T ) @@ -538,7 +540,7 @@ class Covariance: return Covariance(self.__name+"T", asEyeByVector = self.__B ) elif self.isscalar(): return Covariance(self.__name+"T", asEyeByScalar = self.__B ) - + def cholesky(self): if self.ismatrix(): return Covariance(self.__name+"C", asCovariance = numpy.linalg.cholesky(self.__B) ) @@ -546,7 +548,7 @@ class Covariance: return Covariance(self.__name+"C", asEyeByVector = numpy.sqrt( self.__B ) ) elif self.isscalar(): return Covariance(self.__name+"C", asEyeByScalar = numpy.sqrt( self.__B ) ) - + def choleskyI(self): if self.ismatrix(): return Covariance(self.__name+"H", asCovariance = numpy.linalg.cholesky(self.__B).I ) @@ -554,7 +556,7 @@ class Covariance: return Covariance(self.__name+"H", asEyeByVector = 1.0 / numpy.sqrt( self.__B ) ) elif self.isscalar(): return Covariance(self.__name+"H", asEyeByScalar = 1.0 / numpy.sqrt( self.__B ) ) - + def diag(self, msize=None): if self.ismatrix(): return numpy.diag(self.__B) @@ -565,7 +567,7 @@ class Covariance: 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,)) else: return self.__B * numpy.ones(int(msize)) - + def asfullmatrix(self, msize=None): if self.ismatrix(): return self.__B @@ -576,7 +578,7 @@ class Covariance: 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,)) else: return numpy.matrix( self.__B * numpy.eye(int(msize)), float ) - + def trace(self, msize=None): if self.ismatrix(): return numpy.trace(self.__B) @@ -587,13 +589,13 @@ class Covariance: 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,)) else: return self.__B * int(msize) - + def __repr__(self): return repr(self.__B) - + def __str__(self): return str(self.__B) - + def __add__(self, other): if self.ismatrix(): return self.__B + numpy.asmatrix(other) @@ -618,7 +620,7 @@ class Covariance: def __neg__(self): return - self.__B - + def __mul__(self, other): if self.ismatrix() and isinstance(other,numpy.matrix): return self.__B * other diff --git a/src/daComposant/daCore/ExtendedLogging.py b/src/daComposant/daCore/ExtendedLogging.py index d1751aa..3fa494f 100644 --- a/src/daComposant/daCore/ExtendedLogging.py +++ b/src/daComposant/daCore/ExtendedLogging.py @@ -20,10 +20,10 @@ # # Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D -__doc__ = """ +""" Ce module permet de mettre en place un logging utilisable partout dans l'application, par défaut à la console, et si nécessaire dans un fichier. - + Il doit être appelé en premier dans AssimilationStudy (mais pas directement dans les applications utilisateurs), en l'important et en instanciant un objet : @@ -47,7 +47,7 @@ __doc__ = """ log.setLogfile(filename="toto.log", filemode="a", level=logging.WARNING) et on change éventuellement le niveau avec : log.setLogfileLevel(logging.INFO) - + Ensuite, n'importe où dans les applications, il suffit d'utiliser le module "logging" (avec un petit "l") : import logging @@ -66,7 +66,7 @@ __doc__ = """ import logging log = logging.getLogger(NAME) # Avec rien (recommandé) ou un nom NAME log.setLevel(logging.DEBUG) - + On rappelle les niveaux (attributs de "logging") et leur ordre : NOTSET=0 < DEBUG=10 < INFO=20 < WARNING=30 < ERROR=40 < CRITICAL=50 """ @@ -110,7 +110,7 @@ class ExtendedLogging: # Permet de changer globalement le niveau des messages disponibles. # """ # logging.getLogger().setLevel(level) -# +# def setLogfile(self, filename=LOGFILE, filemode="w", level=logging.NOTSET): """ Permet de disposer des messages dans un fichier EN PLUS de la console. @@ -131,7 +131,7 @@ class ExtendedLogging: pris en compte que s'il est supérieur au niveau global. """ self.__logfile.setLevel(level) - + def getLevel(self): """ Renvoie le niveau de logging sous forme texte -- 2.39.2