X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FdaComposant%2FdaCore%2FPersistence.py;h=f155146926ee0f3295cff795c97cbd168e91ebc0;hb=61f19963a2e2ed620bdf577c5a01808586dd938d;hp=858c86551f86c529b0c1cd110b7feb67b91fa8b0;hpb=bad8676749632aa7658cb3e9a94acb9e81208371;p=modules%2Fadao.git diff --git a/src/daComposant/daCore/Persistence.py b/src/daComposant/daCore/Persistence.py index 858c865..f155146 100644 --- a/src/daComposant/daCore/Persistence.py +++ b/src/daComposant/daCore/Persistence.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2008-2018 EDF R&D +# Copyright (C) 2008-2022 EDF R&D # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -21,28 +21,25 @@ # Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D """ - Définit des outils de persistence et d'enregistrement de séries de valeurs + Définit des outils de persistance et d'enregistrement de séries de valeurs pour analyse ultérieure ou utilisation de calcul. """ __author__ = "Jean-Philippe ARGAUD" __all__ = [] -import sys, numpy, copy +import os, numpy, copy, math +import gzip, bz2, pickle from daCore.PlatformInfo import PathManagement ; PathManagement() - -if sys.version_info.major < 3: - range = xrange - iLong = long - import cPickle as pickle -else: - iLong = int - import pickle +from daCore.PlatformInfo import has_gnuplot, PlatformInfo +mfp = PlatformInfo().MaximumPrecision() +if has_gnuplot: + import Gnuplot # ============================================================================== class Persistence(object): """ - Classe générale de persistence définissant les accesseurs nécessaires + Classe générale de persistance définissant les accesseurs nécessaires (Template) """ def __init__(self, name="", unit="", basetype=str): @@ -67,7 +64,6 @@ class Persistence(object): self.__tags = [] # self.__dynamic = False - self.__gnuplot = None self.__g = None self.__title = None self.__ltitle = None @@ -101,7 +97,7 @@ class Persistence(object): def pop(self, item=None): """ - Retire une valeur enregistree par son index de stockage. Sans argument, + Retire une valeur enregistrée par son index de stockage. Sans argument, retire le dernier objet enregistre. """ if item is not None: @@ -143,6 +139,9 @@ class Persistence(object): "x.__len__() <==> len(x)" return len(self.__values) + def name(self): + return self.__name + def __getitem__(self, index=None ): "x.__getitem__(y) <==> x[y]" return copy.copy(self.__values[index]) @@ -248,18 +247,18 @@ class Persistence(object): return allKeys # --------------------------------------------------------- - # Pour compatibilite + # Pour compatibilité def stepnumber(self): "Nombre de pas" return len(self.__values) - # Pour compatibilite + # Pour compatibilité def stepserie(self, **kwargs): "Nombre de pas filtrés" __indexOfFilteredItems = self.__filteredIndexes(**kwargs) return __indexOfFilteredItems - # Pour compatibilite + # Pour compatibilité def steplist(self, **kwargs): "Nombre de pas filtrés" __indexOfFilteredItems = self.__filteredIndexes(**kwargs) @@ -273,7 +272,7 @@ class Persistence(object): élémentaires numpy. """ try: - return [numpy.matrix(item).mean() for item in self.__values] + return [numpy.mean(item, dtype=mfp).astype('float') for item in self.__values] except: raise TypeError("Base type is incompatible with numpy") @@ -288,9 +287,9 @@ class Persistence(object): """ try: if numpy.version.version >= '1.1.0': - return [numpy.matrix(item).std(ddof=ddof) for item in self.__values] + return [numpy.array(item).std(ddof=ddof, dtype=mfp).astype('float') for item in self.__values] else: - return [numpy.matrix(item).std() for item in self.__values] + return [numpy.array(item).std(dtype=mfp).astype('float') for item in self.__values] except: raise TypeError("Base type is incompatible with numpy") @@ -301,7 +300,7 @@ class Persistence(object): numpy. """ try: - return [numpy.matrix(item).sum() for item in self.__values] + return [numpy.array(item).sum() for item in self.__values] except: raise TypeError("Base type is incompatible with numpy") @@ -312,7 +311,7 @@ class Persistence(object): numpy. """ try: - return [numpy.matrix(item).min() for item in self.__values] + return [numpy.array(item).min() for item in self.__values] except: raise TypeError("Base type is incompatible with numpy") @@ -323,10 +322,112 @@ class Persistence(object): numpy. """ try: - return [numpy.matrix(item).max() for item in self.__values] + return [numpy.array(item).max() for item in self.__values] + except: + raise TypeError("Base type is incompatible with numpy") + + def norms(self, _ord=None): + """ + Norm (_ord : voir numpy.linalg.norm) + + Renvoie la série, contenant à chaque pas, la norme des données au pas. + Il faut que le type de base soit compatible avec les types élémentaires + numpy. + """ + try: + return [numpy.linalg.norm(item, _ord) for item in self.__values] except: raise TypeError("Base type is incompatible with numpy") + def maes(self, _predictor=None): + """ + Mean Absolute Error (MAE) + mae(dX) = 1/n sum(dX_i) + + Renvoie la série, contenant à chaque pas, la MAE des données au pas. + Il faut que le type de base soit compatible avec les types élémentaires + numpy. C'est réservé aux variables d'écarts ou d'incréments si le + prédicteur est None, sinon c'est appliqué à l'écart entre les données + au pas et le prédicteur au même pas. + """ + if _predictor is None: + try: + return [numpy.mean(numpy.abs(item)) for item in self.__values] + except: + raise TypeError("Base type is incompatible with numpy") + else: + if len(_predictor) != len(self.__values): + raise ValueError("Predictor number of steps is incompatible with the values") + for i, item in enumerate(self.__values): + if numpy.asarray(_predictor[i]).size != numpy.asarray(item).size: + raise ValueError("Predictor size at step %i is incompatible with the values"%i) + try: + return [numpy.mean(numpy.abs(numpy.ravel(item) - numpy.ravel(_predictor[i]))) for i, item in enumerate(self.__values)] + except: + raise TypeError("Base type is incompatible with numpy") + + def mses(self, _predictor=None): + """ + Mean-Square Error (MSE) ou Mean-Square Deviation (MSD) + mse(dX) = 1/n sum(dX_i**2) + + Renvoie la série, contenant à chaque pas, la MSE des données au pas. Il + faut que le type de base soit compatible avec les types élémentaires + numpy. C'est réservé aux variables d'écarts ou d'incréments si le + prédicteur est None, sinon c'est appliqué à l'écart entre les données + au pas et le prédicteur au même pas. + """ + if _predictor is None: + try: + __n = self.shape()[0] + return [(numpy.linalg.norm(item)**2 / __n) for item in self.__values] + except: + raise TypeError("Base type is incompatible with numpy") + else: + if len(_predictor) != len(self.__values): + raise ValueError("Predictor number of steps is incompatible with the values") + for i, item in enumerate(self.__values): + if numpy.asarray(_predictor[i]).size != numpy.asarray(item).size: + raise ValueError("Predictor size at step %i is incompatible with the values"%i) + try: + __n = self.shape()[0] + return [(numpy.linalg.norm(numpy.ravel(item) - numpy.ravel(_predictor[i]))**2 / __n) for i, item in enumerate(self.__values)] + except: + raise TypeError("Base type is incompatible with numpy") + + msds=mses # Mean-Square Deviation (MSD=MSE) + + def rmses(self, _predictor=None): + """ + Root-Mean-Square Error (RMSE) ou Root-Mean-Square Deviation (RMSD) + rmse(dX) = sqrt( 1/n sum(dX_i**2) ) = sqrt( mse(dX) ) + + Renvoie la série, contenant à chaque pas, la RMSE des données au pas. + Il faut que le type de base soit compatible avec les types élémentaires + numpy. C'est réservé aux variables d'écarts ou d'incréments si le + prédicteur est None, sinon c'est appliqué à l'écart entre les données + au pas et le prédicteur au même pas. + """ + if _predictor is None: + try: + __n = self.shape()[0] + return [(numpy.linalg.norm(item) / math.sqrt(__n)) for item in self.__values] + except: + raise TypeError("Base type is incompatible with numpy") + else: + if len(_predictor) != len(self.__values): + raise ValueError("Predictor number of steps is incompatible with the values") + for i, item in enumerate(self.__values): + if numpy.asarray(_predictor[i]).size != numpy.asarray(item).size: + raise ValueError("Predictor size at step %i is incompatible with the values"%i) + try: + __n = self.shape()[0] + return [(numpy.linalg.norm(numpy.ravel(item) - numpy.ravel(_predictor[i])) / math.sqrt(__n)) for i, item in enumerate(self.__values)] + except: + raise TypeError("Base type is incompatible with numpy") + + rmsds = rmses # Root-Mean-Square Deviation (RMSD=RMSE) + def __preplots(self, title = "", xlabel = "", @@ -339,21 +440,18 @@ class Persistence(object): "Préparation des plots" # # Vérification de la disponibilité du module Gnuplot - try: - import Gnuplot - self.__gnuplot = Gnuplot - except: + if not has_gnuplot: raise ImportError("The Gnuplot module is required to plot the object.") # # Vérification et compléments sur les paramètres d'entrée if persist: - self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry + Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry else: - self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry + Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry if ltitle is None: ltitle = "" - self.__g = self.__gnuplot.Gnuplot() # persist=1 - self.__g('set terminal '+self.__gnuplot.GnuplotOpts.default_term) + self.__g = Gnuplot.Gnuplot() # persist=1 + self.__g('set terminal '+Gnuplot.GnuplotOpts.default_term) self.__g('set style data lines') self.__g('set grid') self.__g('set autoscale') @@ -412,7 +510,6 @@ class Persistence(object): attendant un Return Par défaut, pause = True """ - import os if not self.__dynamic: self.__preplots(title, xlabel, ylabel, ltitle, geometry, persist, pause ) if dynamic: @@ -436,7 +533,7 @@ class Persistence(object): else: Steps = list(range(len(self.__values[index]))) # - self.__g.plot( self.__gnuplot.Data( Steps, self.__values[index], title=ltitle ) ) + self.__g.plot( Gnuplot.Data( Steps, self.__values[index], title=ltitle ) ) # if filename != "": i += 1 @@ -455,12 +552,13 @@ class Persistence(object): # self.__g('set title "'+str(self.__title)) Steps = list(range(len(self.__values))) - self.__g.plot( self.__gnuplot.Data( Steps, self.__values, title=self.__ltitle ) ) + self.__g.plot( Gnuplot.Data( Steps, self.__values, title=self.__ltitle ) ) # if self.__pause: eval(input('Please press return to continue...\n')) # --------------------------------------------------------- + # On pourrait aussi utiliser d'autres attributs d'un "array" comme "tofile" def mean(self): """ Renvoie la moyenne sur toutes les valeurs sans tenir compte de la @@ -468,10 +566,7 @@ class Persistence(object): les types élémentaires numpy. """ try: - if self.__basetype in [int, float]: - return float( numpy.array(self.__values).mean() ) - else: - return numpy.array(self.__values).mean(axis=0) + return numpy.mean(self.__values, axis=0, dtype=mfp).astype('float') except: raise TypeError("Base type is incompatible with numpy") @@ -486,9 +581,9 @@ class Persistence(object): """ try: if numpy.version.version >= '1.1.0': - return numpy.array(self.__values).std(ddof=ddof,axis=0) + return numpy.asarray(self.__values).std(ddof=ddof,axis=0).astype('float') else: - return numpy.array(self.__values).std(axis=0) + return numpy.asarray(self.__values).std(axis=0).astype('float') except: raise TypeError("Base type is incompatible with numpy") @@ -499,7 +594,7 @@ class Persistence(object): les types élémentaires numpy. """ try: - return numpy.array(self.__values).sum(axis=0) + return numpy.asarray(self.__values).sum(axis=0) except: raise TypeError("Base type is incompatible with numpy") @@ -510,7 +605,7 @@ class Persistence(object): les types élémentaires numpy. """ try: - return numpy.array(self.__values).min(axis=0) + return numpy.asarray(self.__values).min(axis=0) except: raise TypeError("Base type is incompatible with numpy") @@ -521,7 +616,7 @@ class Persistence(object): les types élémentaires numpy. """ try: - return numpy.array(self.__values).max(axis=0) + return numpy.asarray(self.__values).max(axis=0) except: raise TypeError("Base type is incompatible with numpy") @@ -532,13 +627,10 @@ class Persistence(object): les types élémentaires numpy. """ try: - return numpy.array(self.__values).cumsum(axis=0) + return numpy.asarray(self.__values).cumsum(axis=0) except: raise TypeError("Base type is incompatible with numpy") - # On pourrait aussi utiliser les autres attributs d'une "matrix", comme - # "tofile", "min"... - def plot(self, steps = None, title = "", @@ -578,25 +670,22 @@ class Persistence(object): """ # # Vérification de la disponibilité du module Gnuplot - try: - import Gnuplot - self.__gnuplot = Gnuplot - except: + if not has_gnuplot: raise ImportError("The Gnuplot module is required to plot the object.") # # Vérification et compléments sur les paramètres d'entrée if persist: - self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry + Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry else: - self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry + Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry if ltitle is None: ltitle = "" if isinstance(steps,list) or isinstance(steps, numpy.ndarray): Steps = list(steps) else: Steps = list(range(len(self.__values[0]))) - self.__g = self.__gnuplot.Gnuplot() # persist=1 - self.__g('set terminal '+self.__gnuplot.GnuplotOpts.default_term) + self.__g = Gnuplot.Gnuplot() # persist=1 + self.__g('set terminal '+Gnuplot.GnuplotOpts.default_term) self.__g('set style data lines') self.__g('set grid') self.__g('set autoscale') @@ -606,9 +695,9 @@ class Persistence(object): # # Tracé du ou des vecteurs demandés indexes = list(range(len(self.__values))) - self.__g.plot( self.__gnuplot.Data( Steps, self.__values[indexes.pop(0)], title=ltitle+" (pas 0)" ) ) + self.__g.plot( Gnuplot.Data( Steps, self.__values[indexes.pop(0)], title=ltitle+" (pas 0)" ) ) for index in indexes: - self.__g.replot( self.__gnuplot.Data( Steps, self.__values[index], title=ltitle+" (pas %i)"%index ) ) + self.__g.replot( Gnuplot.Data( Steps, self.__values[index], title=ltitle+" (pas %i)"%index ) ) # if filename != "": self.__g.hardcopy(filename=filename, color=1) @@ -632,7 +721,7 @@ class Persistence(object): elif isinstance(Scheduler,range): # Considéré comme un itérateur Schedulers = Scheduler elif isinstance(Scheduler,(list,tuple)): # Considéré comme des index explicites - Schedulers = [iLong(i) for i in Scheduler] # map( long, Scheduler ) + Schedulers = [int(i) for i in Scheduler] # map( long, Scheduler ) else: # Dans tous les autres cas, activé par défaut Schedulers = range( 0, maxiter ) # @@ -670,6 +759,21 @@ class Persistence(object): def hasDataObserver(self): return bool(len(self.__dataobservers) > 0) +# ============================================================================== +class SchedulerTrigger(object): + """ + Classe générale d'interface de type Scheduler/Trigger + """ + def __init__(self, + simplifiedCombo = None, + startTime = 0, + endTime = int( 1e9 ), + timeDelay = 1, + timeUnit = 1, + frequency = None, + ): + pass + # ============================================================================== class OneScalar(Persistence): """ @@ -701,15 +805,15 @@ class OneVector(Persistence): class OneMatrix(Persistence): """ - Classe de stockage d'une matrice de valeurs (numpy.matrix) par pas. + Classe de stockage d'une matrice de valeurs homogènes par pas. """ def __init__(self, name="", unit="", basetype = numpy.matrix): Persistence.__init__(self, name, unit, basetype) class OneList(Persistence): """ - Classe de stockage d'une liste de valeurs hétérogènes (list) par pas. Ne pas - utiliser cette classe pour des données numériques homogènes, mais + Classe de stockage d'une liste de valeurs hétérogènes (list) par pas. Ne + pas utiliser cette classe pour des données numériques homogènes, mais "OneVector". """ def __init__(self, name="", unit="", basetype = list): @@ -723,8 +827,8 @@ class OneNoType(Persistence): """ Classe de stockage d'un objet sans modification (cast) de type. Attention, selon le véritable type de l'objet stocké à chaque pas, les opérations - arithmétiques à base de numpy peuvent être invalides ou donner des résultats - inattendus. Cette classe n'est donc à utiliser qu'à bon escient + arithmétiques à base de numpy peuvent être invalides ou donner des + résultats inattendus. Cette classe n'est donc à utiliser qu'à bon escient volontairement, et pas du tout par défaut. """ def __init__(self, name="", unit="", basetype = NoType): @@ -743,9 +847,9 @@ class CompositePersistence(object): """ name : nom courant - La gestion interne des données est exclusivement basée sur les variables - initialisées ici (qui ne sont pas accessibles depuis l'extérieur des - objets comme des attributs) : + La gestion interne des données est exclusivement basée sur les + variables initialisées ici (qui ne sont pas accessibles depuis + l'extérieur des objets comme des attributs) : __StoredObjects : objets de type persistence collectés dans cet objet """ self.__name = str(name) @@ -781,8 +885,8 @@ class CompositePersistence(object): def add_object(self, name=None, persistenceType=Persistence, basetype=None ): """ - Ajoute dans les objets stockables un nouvel objet défini par son nom, son - type de Persistence et son type de base à chaque pas. + Ajoute dans les objets stockables un nouvel objet défini par son nom, + son type de Persistence et son type de base à chaque pas. """ if name is None: raise ValueError("Object name is required for adding an object.") if name in self.__StoredObjects.keys(): @@ -865,7 +969,6 @@ class CompositePersistence(object): Enregistre l'objet dans le fichier indiqué selon le "mode" demandé, et renvoi le nom du fichier """ - import os if filename is None: if compress == "gzip": filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.gz" @@ -878,10 +981,8 @@ class CompositePersistence(object): # if mode == "pickle": if compress == "gzip": - import gzip output = gzip.open( filename, 'wb') elif compress == "bzip2": - import bz2 output = bz2.BZ2File( filename, 'wb') else: output = open( filename, 'wb') @@ -896,7 +997,6 @@ class CompositePersistence(object): """ Recharge un objet composite sauvé en fichier """ - import os if filename is None: raise ValueError("A file name if requested to load a composite.") else: @@ -904,10 +1004,8 @@ class CompositePersistence(object): # if mode == "pickle": if compress == "gzip": - import gzip pkl_file = gzip.open( filename, 'rb') elif compress == "bzip2": - import bz2 pkl_file = bz2.BZ2File( filename, 'rb') else: pkl_file = open(filename, 'rb') @@ -921,4 +1019,4 @@ class CompositePersistence(object): # ============================================================================== if __name__ == "__main__": - print('\n AUTODIAGNOSTIC \n') + print('\n AUTODIAGNOSTIC\n')