Salome HOME
Minor source corrections for compatibility
[modules/adao.git] / src / daComposant / daNumerics / ApproximatedDerivatives.py
index 73160cbb8cf84f967a08228103885b2753ede5a0..bdf9d862e55bfca071766fd037a646f1f4b7862c 100644 (file)
@@ -1,48 +1,71 @@
 #-*-coding:iso-8859-1-*-
 #
-#  Copyright (C) 2008-2012 EDF R&D
+# Copyright (C) 2008-2013 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
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
 #
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
 #
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 #
-#  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 #
-#  Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D
+# Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D
 
 __doc__ = """
     Définit les versions approximées des opérateurs tangents et adjoints.
 """
 __author__ = "Jean-Philippe ARGAUD"
 
-import os, numpy, time
+import os, numpy, time, copy
 import logging
-# logging.getLogger().setLevel(logging.DEBUG)
+# logging.getLogger().setLevel(logging.DEBUG)
 
 # ==============================================================================
 class FDApproximation:
     """
     Cette classe sert d'interface pour définir les opérateurs approximés. A la
-    création d'un objet, en fournissant une fonction "FunctionH", on obtient un
-    objet qui dispose de 3 méthodes "FunctionH", "TangentH" et "AdjointH". On
-    contrôle l'approximation DF avec l'incrément multiplicatif "increment"
-    valant par défaut 1%, ou avec l'incrément fixe "dX" qui sera multiplié par
-    "increment" (donc en %), et on effectue de DF centrées si le booléen
-    "centeredDF" est vrai.
+    création d'un objet, en fournissant une fonction "Function", on obtient un
+    objet qui dispose de 3 méthodes "DirectOperator", "TangentOperator" et
+    "AdjointOperator". On contrôle l'approximation DF avec l'incrément
+    multiplicatif "increment" valant par défaut 1%, ou avec l'incrément fixe
+    "dX" qui sera multiplié par "increment" (donc en %), et on effectue de DF
+    centrées si le booléen "centeredDF" est vrai.
     """
-    def __init__(self, FunctionH = None, centeredDF = False, increment = 0.01, dX = None):
-        self.FunctionH    = FunctionH
+    def __init__(self,
+            Function              = None,
+            centeredDF            = False,
+            increment             = 0.01,
+            dX                    = None,
+            avoidingRedundancy    = True,
+            toleranceInRedundancy = 1.e-18,
+            lenghtOfRedundancy    = -1,
+            ):
+        self.__userFunction = Function
         self.__centeredDF = bool(centeredDF)
+        if avoidingRedundancy:
+            self.__avoidRC = True
+            self.__tolerBP = float(toleranceInRedundancy)
+            self.__lenghtRH = int(lenghtOfRedundancy)
+            self.__lenghtRJ = int(lenghtOfRedundancy)
+            self.__listDPCP = [] # Direct Operator Previous Calculated Points
+            self.__listDPCR = [] # Direct Operator Previous Calculated Results
+            self.__listDPCN = [] # Direct Operator Previous Calculated Point Norms
+            self.__listJPCP = [] # Jacobian Previous Calculated Points
+            self.__listJPCI = [] # Jacobian Previous Calculated Increment
+            self.__listJPCR = [] # Jacobian Previous Calculated Results
+            self.__listJPPN = [] # Jacobian Previous Calculated Point Norms
+            self.__listJPIN = [] # Jacobian Previous Calculated Increment Norms
+        else:
+            self.__avoidRC = False
         if float(increment) <> 0.:
             self.__increment  = float(increment)
         else:
@@ -50,16 +73,59 @@ class FDApproximation:
         if dX is None:  
             self.__dX     = None
         else:
-            self.__dX     = numpy.asmatrix(dX).flatten().T
+            self.__dX     = numpy.asmatrix(numpy.ravel( dX )).T
+        logging.debug("FDA Reduction des doublons de calcul : %s"%self.__avoidRC)
+        if self.__avoidRC:
+            logging.debug("FDA Tolerance de determination des doublons : %.2e"%self.__tolerBP)
+
+    # ---------------------------------------------------------
+    def __doublon__(self, e, l, n, v=""):
+        __ac, __iac = False, -1
+        for i in xrange(len(l)-1,-1,-1):
+            if numpy.linalg.norm(e - l[i]) < self.__tolerBP * n[i]:
+                __ac, __iac = True, i
+                if v is not None: logging.debug("FDA Cas%s déja calculé, récupération du doublon %i"%(v,__iac))
+                break
+        return __ac, __iac
 
     # ---------------------------------------------------------
-    def TangentHMatrix(self, X ):
+    def DirectOperator(self, X ):
+        """
+        Calcul du direct à l'aide de la fonction fournie.
+        """
+        _X = numpy.asmatrix(numpy.ravel( X )).T
+        #
+        __alreadyCalculated = False
+        if self.__avoidRC:
+            __alreadyCalculated, __i = self.__doublon__(_X, self.__listDPCP, self.__listDPCN, " H")
+        #
+        if __alreadyCalculated:
+            logging.debug("FDA Calcul DirectOperator (par récupération du doublon %i)"%__i)
+            _HX = self.__listDPCR[__i]
+        else:
+            logging.debug("FDA Calcul DirectOperator (explicite)")
+            _HX = numpy.ravel(self.__userFunction( _X ))
+            if self.__avoidRC:
+                if self.__lenghtRH < 0: self.__lenghtRH = 2 * _X.size
+                if len(self.__listDPCP) > self.__lenghtRH:
+                    logging.debug("FDA Réduction de la liste de H à %i éléments"%self.__lenghtRH)
+                    self.__listDPCP.pop(0)
+                    self.__listDPCR.pop(0)
+                    self.__listDPCN.pop(0)
+                self.__listDPCP.append( copy.copy(_X) )
+                self.__listDPCR.append( copy.copy(_HX) )
+                self.__listDPCN.append( numpy.linalg.norm(_X) )
+        #
+        return _HX
+
+    # ---------------------------------------------------------
+    def TangentMatrix(self, X ):
         """
         Calcul de l'opérateur tangent comme la Jacobienne par différences finies,
         c'est-à-dire le gradient de H en X. On utilise des différences finies
         directionnelles autour du point X. X est un numpy.matrix.
         
-        Différences finies centrées :
+        Différences finies centrées (approximation d'ordre 2):
         1/ Pour chaque composante i de X, on ajoute et on enlève la perturbation
            dX[i] à la  composante X[i], pour composer X_plus_dXi et X_moins_dXi, et
            on calcule les réponses HX_plus_dXi = H( X_plus_dXi ) et HX_moins_dXi =
@@ -68,7 +134,7 @@ class FDApproximation:
            le pas 2*dXi
         3/ Chaque résultat, par composante, devient une colonne de la Jacobienne
         
-        Différences finies non centrées :
+        Différences finies non centrées (approximation d'ordre 1):
         1/ Pour chaque composante i de X, on ajoute la perturbation dX[i] à la 
            composante X[i] pour composer X_plus_dXi, et on calcule la réponse
            HX_plus_dXi = H( X_plus_dXi )
@@ -78,17 +144,19 @@ class FDApproximation:
         4/ Chaque résultat, par composante, devient une colonne de la Jacobienne
         
         """
-        logging.debug("  == Calcul de la Jacobienne")
-        logging.debug("     Incrément de............: %s*X"%float(self.__increment))
-        logging.debug("     Approximation centrée...: %s"%(self.__centeredDF))
+        logging.debug("FDA Calcul de la Jacobienne")
+        logging.debug("FDA   Incrément de............: %s*X"%float(self.__increment))
+        logging.debug("FDA   Approximation centrée...: %s"%(self.__centeredDF))
+        #
+        if X is None or len(X)==0:
+            raise ValueError("Nominal point X for approximate derivatives can not be None or void.")
         #
-        _X = numpy.asmatrix(X).flatten().T
+        _X = numpy.asmatrix(numpy.ravel( X )).T
         #
         if self.__dX is None:
             _dX  = self.__increment * _X
         else:
-            _dX = numpy.asmatrix(self.__dX).flatten().T
-        logging.debug("     Incrément non corrigé...: %s"%(_dX))
+            _dX = numpy.asmatrix(numpy.ravel( self.__dX )).T
         #
         if (_dX == 0.).any():
             moyenne = _dX.mean()
@@ -96,148 +164,107 @@ class FDApproximation:
                 _dX = numpy.where( _dX == 0., float(self.__increment), _dX )
             else:
                 _dX = numpy.where( _dX == 0., moyenne, _dX )
-        logging.debug("     Incrément dX............: %s"%(_dX))
         #
-        if self.__centeredDF:
-            #
-            # Boucle de calcul des colonnes de la Jacobienne
-            # ----------------------------------------------
-            Jacobienne  = []
-            for i in range( len(_dX) ):
-                X_plus_dXi     = numpy.array( _X.A1, dtype=float )
-                X_plus_dXi[i]  = _X[i] + _dX[i]
-                X_moins_dXi    = numpy.array( _X.A1, dtype=float )
-                X_moins_dXi[i] = _X[i] - _dX[i]
-                #
-                HX_plus_dXi  = self.FunctionH( X_plus_dXi )
-                HX_moins_dXi = self.FunctionH( X_moins_dXi )
+        __alreadyCalculated  = False
+        if self.__avoidRC:
+            __bidon, __alreadyCalculatedP = self.__doublon__(_X,  self.__listJPCP, self.__listJPPN, None)
+            __bidon, __alreadyCalculatedI = self.__doublon__(_dX, self.__listJPCI, self.__listJPIN, None)
+            if __alreadyCalculatedP == __alreadyCalculatedI > -1:
+                __alreadyCalculated, __i = True, __alreadyCalculatedP
+                logging.debug("FDA Cas J déja calculé, récupération du doublon %i"%__i)
+        #
+        if __alreadyCalculated:
+            logging.debug("FDA   Calcul Jacobienne (par récupération du doublon %i)"%__i)
+            _Jacobienne = self.__listJPCR[__i]
+        else:
+            logging.debug("FDA   Calcul Jacobienne (explicite)")
+            if self.__centeredDF:
                 #
-                HX_Diff = ( HX_plus_dXi - HX_moins_dXi ) / (2.*_dX[i])
+                _Jacobienne  = []
+                for i in range( _dX.size ):
+                    _dXi            = _dX[i]
+                    _X_plus_dXi     = numpy.array( _X.A1, dtype=float )
+                    _X_plus_dXi[i]  = _X[i] + _dXi
+                    _X_moins_dXi    = numpy.array( _X.A1, dtype=float )
+                    _X_moins_dXi[i] = _X[i] - _dXi
+                    #
+                    _HX_plus_dXi    = self.DirectOperator( _X_plus_dXi )
+                    _HX_moins_dXi   = self.DirectOperator( _X_moins_dXi )
+                    #
+                    _Jacobienne.append( numpy.ravel( _HX_plus_dXi - _HX_moins_dXi ) / (2.*_dXi) )
                 #
-                Jacobienne.append( HX_Diff )
-            #
-        else:
-            #
-            # Boucle de calcul des colonnes de la Jacobienne
-            # ----------------------------------------------
-            HX_plus_dX = []
-            for i in range( len(_dX) ):
-                X_plus_dXi    = numpy.array( _X.A1, dtype=float )
-                X_plus_dXi[i] = _X[i] + _dX[i]
+            else:
                 #
-                HX_plus_dXi = self.FunctionH( X_plus_dXi )
+                _Jacobienne  = []
+                _HX = self.DirectOperator( _X )
+                for i in range( _dX.size ):
+                    _dXi            = _dX[i]
+                    _X_plus_dXi     = numpy.array( _X.A1, dtype=float )
+                    _X_plus_dXi[i]  = _X[i] + _dXi
+                    #
+                    _HX_plus_dXi = self.DirectOperator( _X_plus_dXi )
+                    #
+                    _Jacobienne.append( numpy.ravel(( _HX_plus_dXi - _HX ) / _dXi) )
                 #
-                HX_plus_dX.append( HX_plus_dXi )
             #
-            # Calcul de la valeur centrale
-            # ----------------------------
-            HX = self.FunctionH( _X )
-            #
-            # Calcul effectif de la Jacobienne par différences finies
-            # -------------------------------------------------------
-            Jacobienne = []
-            for i in range( len(_dX) ):
-                Jacobienne.append( numpy.asmatrix(( HX_plus_dX[i] - HX ) / _dX[i]).flatten().A1 )
+            _Jacobienne = numpy.matrix( numpy.vstack( _Jacobienne ) ).T
+            if self.__avoidRC:
+                if self.__lenghtRJ < 0: self.__lenghtRJ = 2 * _X.size
+                if len(self.__listJPCP) > self.__lenghtRJ:
+                    logging.debug("FDA Réduction de la liste de J à %i éléments"%self.__lenghtRJ)
+                    self.__listJPCP.pop(0)
+                    self.__listJPCI.pop(0)
+                    self.__listJPCR.pop(0)
+                    self.__listJPPN.pop(0)
+                    self.__listJPIN.pop(0)
+                self.__listJPCP.append( copy.copy(_X) )
+                self.__listJPCI.append( copy.copy(_dX) )
+                self.__listJPCR.append( copy.copy(_Jacobienne) )
+                self.__listJPPN.append( numpy.linalg.norm(_X) )
+                self.__listJPIN.append( numpy.linalg.norm(_Jacobienne) )
         #
-        Jacobienne = numpy.matrix( numpy.vstack( Jacobienne ) ).T
-        logging.debug("  == Fin du calcul de la Jacobienne")
+        logging.debug("FDA Fin du calcul de la Jacobienne")
         #
-        return Jacobienne
+        return _Jacobienne
 
     # ---------------------------------------------------------
-    def TangentH(self, (X, dX) ):
+    def TangentOperator(self, (X, dX) ):
         """
         Calcul du tangent à l'aide de la Jacobienne.
         """
-        Jacobienne = self.TangentHMatrix( X )
+        _Jacobienne = self.TangentMatrix( X )
         if dX is None or len(dX) == 0:
             #
             # Calcul de la forme matricielle si le second argument est None
             # -------------------------------------------------------------
-            return Jacobienne
+            return _Jacobienne
         else:
             #
             # Calcul de la valeur linéarisée de H en X appliqué à dX
             # ------------------------------------------------------
-            _dX = numpy.asmatrix(dX).flatten().T
-            HtX = numpy.dot(Jacobienne, _dX)
-            return HtX.A1
+            _dX = numpy.asmatrix(numpy.ravel( dX )).T
+            _HtX = numpy.dot(_Jacobienne, _dX)
+            return _HtX.A1
 
     # ---------------------------------------------------------
-    def AdjointH(self, (X, Y) ):
+    def AdjointOperator(self, (X, Y) ):
         """
         Calcul de l'adjoint à l'aide de la Jacobienne.
         """
-        JacobienneT = self.TangentHMatrix( X ).T
+        _JacobienneT = self.TangentMatrix( X ).T
         if Y is None or len(Y) == 0:
             #
             # Calcul de la forme matricielle si le second argument est None
             # -------------------------------------------------------------
-            return JacobienneT
+            return _JacobienneT
         else:
             #
             # Calcul de la valeur de l'adjoint en X appliqué à Y
             # --------------------------------------------------
-            _Y = numpy.asmatrix(Y).flatten().T
-            HaY = numpy.dot(JacobienneT, _Y)
-            return HaY.A1
-
-# ==============================================================================
-#
-def test1FunctionH( XX ):
-    """ Direct non-linear simulation operator """
-    #
-    # NEED TO BE COMPLETED
-    # NEED TO BE COMPLETED
-    # NEED TO BE COMPLETED
-    #
-    # --------------------------------------> # EXAMPLE TO BE REMOVED
-    # Example of Identity operator            # EXAMPLE TO BE REMOVED
-    if type(XX) is type(numpy.matrix([])):    # EXAMPLE TO BE REMOVED
-        HX = XX.A1.tolist()                   # EXAMPLE TO BE REMOVED
-    elif type(XX) is type(numpy.array([])):   # EXAMPLE TO BE REMOVED
-        HX = numpy.matrix(XX).A1.tolist()     # EXAMPLE TO BE REMOVED
-    else:                                     # EXAMPLE TO BE REMOVED
-        HX = XX                               # EXAMPLE TO BE REMOVED
-    #                                         # EXAMPLE TO BE REMOVED
-    HHX = []                                  # EXAMPLE TO BE REMOVED
-    HHX.extend( HX )                          # EXAMPLE TO BE REMOVED
-    HHX.extend( HX )                          # EXAMPLE TO BE REMOVED
-    # --------------------------------------> # EXAMPLE TO BE REMOVED
-    #
-    return numpy.array( HHX )
+            _Y = numpy.asmatrix(numpy.ravel( Y )).T
+            _HaY = numpy.dot(_JacobienneT, _Y)
+            return _HaY.A1
 
 # ==============================================================================
 if __name__ == "__main__":
-
-    print
-    print "AUTODIAGNOSTIC"
-    print "=============="
-    
-    X0 = [1, 2, 3]
-    FDA = FDApproximation( test1FunctionH )
-    print "H(X)       =",   FDA.FunctionH( X0 )
-    print "Tg matrice =\n", FDA.TangentHMatrix( X0 )
-    print "Tg(X)      =",   FDA.TangentH( (X0, X0) )
-    print "Ad((X,Y))  =",   FDA.AdjointH( (X0,range(3,3+2*len(X0))) )
-    print
-    del FDA
-    FDA = FDApproximation( test1FunctionH, centeredDF=True )
-    print "H(X)       =",   FDA.FunctionH( X0 )
-    print "Tg matrice =\n", FDA.TangentHMatrix( X0 )
-    print "Tg(X)      =",   FDA.TangentH( (X0, X0) )
-    print "Ad((X,Y))  =",   FDA.AdjointH( (X0,range(3,3+2*len(X0))) )
-    print
-    del FDA
-
-    X0 = range(5)
-    FDA = FDApproximation( test1FunctionH )
-    print "H(X)       =",   FDA.FunctionH( X0 )
-    print "Tg matrice =\n", FDA.TangentHMatrix( X0 )
-    print "Tg(X)      =",   FDA.TangentH( (X0, X0) )
-    print "Ad((X,Y))  =",   FDA.AdjointH( (X0,range(7,7+2*len(X0))) )
-    print
-    del FDA
+    print '\n AUTODIAGNOSTIC \n'