Salome HOME
Code improvements, review and simplifications (2)
[modules/adao.git] / src / daComposant / daCore / Persistence.py
index ecaa30405a19dfea13b726bcce618b72b75c94c0..f155146926ee0f3295cff795c97cbd168e91ebc0 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2008-2020 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
 # 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 os, sys, numpy, copy
-import gzip, bz2
+import os, numpy, copy, math
+import gzip, bz2, pickle
 
 from daCore.PlatformInfo import PathManagement ; PathManagement()
 from daCore.PlatformInfo import has_gnuplot, PlatformInfo
@@ -36,18 +36,10 @@ mfp = PlatformInfo().MaximumPrecision()
 if has_gnuplot:
     import Gnuplot
 
-if sys.version_info.major < 3:
-    range = xrange
-    iLong = long
-    import cPickle as pickle
-else:
-    iLong = int
-    import pickle
-
 # ==============================================================================
 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):
@@ -105,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:
@@ -255,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)
@@ -280,7 +272,7 @@ class Persistence(object):
         élémentaires numpy.
         """
         try:
-            return [numpy.mean(item, dtype=mfp) 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")
 
@@ -295,9 +287,9 @@ class Persistence(object):
         """
         try:
             if numpy.version.version >= '1.1.0':
-                return [numpy.array(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.array(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")
 
@@ -334,6 +326,108 @@ class Persistence(object):
         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   = "",
@@ -472,10 +566,7 @@ class Persistence(object):
         les types élémentaires numpy.
         """
         try:
-            if self.__basetype in [int, float]:
-                return float( numpy.mean(self.__values, dtype=mfp) )
-            else:
-                return numpy.mean(self.__values, axis=0, dtype=mfp)
+            return numpy.mean(self.__values, axis=0, dtype=mfp).astype('float')
         except:
             raise TypeError("Base type is incompatible with numpy")
 
@@ -490,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")
 
@@ -503,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")
 
@@ -514,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")
 
@@ -525,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")
 
@@ -536,7 +627,7 @@ 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")
 
@@ -630,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 )
         #
@@ -714,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):
@@ -736,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):
@@ -756,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)
@@ -794,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():