From 4c5ac465c6f6f1f694c31a234306672ca519a441 Mon Sep 17 00:00:00 2001 From: Jean-Philippe ARGAUD Date: Thu, 14 Oct 2021 20:46:33 +0200 Subject: [PATCH] Internal modifications and performance improvements --- src/daComposant/daCore/Aidsm.py | 18 ++--- src/daComposant/daCore/BasicObjects.py | 52 +++++++++---- src/daComposant/daCore/Interfaces.py | 6 +- src/daComposant/daCore/NumericObjects.py | 99 ++++++++++++++++-------- 4 files changed, 113 insertions(+), 62 deletions(-) diff --git a/src/daComposant/daCore/Aidsm.py b/src/daComposant/daCore/Aidsm.py index 6937f32..1875aef 100644 --- a/src/daComposant/daCore/Aidsm.py +++ b/src/daComposant/daCore/Aidsm.py @@ -99,7 +99,6 @@ class Aidsm(object): Concept = None, # Premier argument Algorithm = None, AppliedInXb = None, - AvoidRC = True, Checked = False, ColMajor = False, ColNames = None, @@ -113,6 +112,7 @@ class Aidsm(object): ObjectMatrix = None, OneFunction = None, Parameters = None, + PerformanceProfile = None, ScalarSparseMatrix = None, Scheduler = None, Script = None, @@ -156,13 +156,13 @@ class Aidsm(object): self.setObservationOperator( Matrix, OneFunction, ThreeFunctions, AppliedInXb, Parameters, Script, ExtraArguments, - Stored, AvoidRC, InputFunctionAsMulti, Checked ) + Stored, PerformanceProfile, InputFunctionAsMulti, Checked ) elif Concept in ("EvolutionModel", "ControlModel"): commande = getattr(self,"set"+Concept) commande( Matrix, OneFunction, ThreeFunctions, Parameters, Script, Scheduler, ExtraArguments, - Stored, AvoidRC, InputFunctionAsMulti, Checked ) + Stored, PerformanceProfile, InputFunctionAsMulti, Checked ) else: raise ValueError("the variable named '%s' is not allowed."%str(Concept)) except Exception as e: @@ -368,7 +368,7 @@ class Aidsm(object): Script = None, ExtraArguments = None, Stored = False, - AvoidRC = True, + PerformanceProfile = None, InputFunctionAsMulti = False, Checked = False): "Definition d'un concept de calcul" @@ -383,7 +383,7 @@ class Aidsm(object): asDict = Parameters, appliedInX = AppliedInXb, extraArguments = ExtraArguments, - avoidRC = AvoidRC, + performancePrf = PerformanceProfile, inputAsMF = InputFunctionAsMulti, scheduledBy = None, toBeChecked = Checked, @@ -401,7 +401,7 @@ class Aidsm(object): Scheduler = None, ExtraArguments = None, Stored = False, - AvoidRC = True, + PerformanceProfile = None, InputFunctionAsMulti = False, Checked = False): "Definition d'un concept de calcul" @@ -416,7 +416,7 @@ class Aidsm(object): asDict = Parameters, appliedInX = None, extraArguments = ExtraArguments, - avoidRC = AvoidRC, + performancePrf = PerformanceProfile, inputAsMF = InputFunctionAsMulti, scheduledBy = Scheduler, toBeChecked = Checked, @@ -434,7 +434,7 @@ class Aidsm(object): Scheduler = None, ExtraArguments = None, Stored = False, - AvoidRC = True, + PerformanceProfile = None, InputFunctionAsMulti = False, Checked = False): "Definition d'un concept de calcul" @@ -449,7 +449,7 @@ class Aidsm(object): asDict = Parameters, appliedInX = None, extraArguments = ExtraArguments, - avoidRC = AvoidRC, + performancePrf = PerformanceProfile, inputAsMF = InputFunctionAsMulti, scheduledBy = Scheduler, toBeChecked = Checked, diff --git a/src/daComposant/daCore/BasicObjects.py b/src/daComposant/daCore/BasicObjects.py index 4f41743..1293b96 100644 --- a/src/daComposant/daCore/BasicObjects.py +++ b/src/daComposant/daCore/BasicObjects.py @@ -125,6 +125,7 @@ class Operator(object): fromMethod = None, fromMatrix = None, avoidingRedundancy = True, + reducingMemoryUse = False, inputAsMultiFunction = False, enableMultiProcess = False, extraArguments = None, @@ -138,6 +139,8 @@ class Operator(object): - fromMethod : argument de type fonction Python - fromMatrix : argument adapté au constructeur numpy.matrix - avoidingRedundancy : booléen évitant (ou pas) les calculs redondants + - reducingMemoryUse : booléen forçant (ou pas) des calculs moins + gourmands en mémoire - inputAsMultiFunction : booléen indiquant une fonction explicitement définie (ou pas) en multi-fonction - extraArguments : arguments supplémentaires passés à la fonction de @@ -145,7 +148,8 @@ class Operator(object): """ self.__name = str(name) self.__NbCallsAsMatrix, self.__NbCallsAsMethod, self.__NbCallsOfCached = 0, 0, 0 - self.__AvoidRC = bool( avoidingRedundancy ) + self.__reduceM = bool( reducingMemoryUse ) + self.__avoidRC = bool( avoidingRedundancy ) self.__inputAsMF = bool( inputAsMultiFunction ) self.__mpEnabled = bool( enableMultiProcess ) self.__extraArgs = extraArguments @@ -172,7 +176,7 @@ class Operator(object): def enableAvoidingRedundancy(self): "Active le cache" - if self.__AvoidRC: + if self.__avoidRC: Operator.CM.enable() else: Operator.CM.disable() @@ -208,14 +212,14 @@ class Operator(object): _HxValue = [] for i in range(len(_HValue)): _HxValue.append( numpy.asmatrix( numpy.ravel( _HValue[i] ) ).T ) - if self.__AvoidRC: + if self.__avoidRC: Operator.CM.storeValueInX(_xValue[i],_HxValue[-1],self.__name) else: _HxValue = [] _xserie = [] _hindex = [] for i, xv in enumerate(_xValue): - if self.__AvoidRC: + if self.__avoidRC: __alreadyCalculated, __HxV = Operator.CM.wasCalculatedIn(xv,self.__name) else: __alreadyCalculated = False @@ -246,7 +250,7 @@ class Operator(object): _xv = _xserie.pop(0) _hv = _hserie.pop(0) _HxValue[i] = _hv - if self.__AvoidRC: + if self.__avoidRC: Operator.CM.storeValueInX(_xv,_hv,self.__name) # if returnSerieAsArrayMatrix: @@ -417,7 +421,7 @@ class FullOperator(object): asDict = None, # Parameters appliedInX = None, extraArguments = None, - avoidRC = True, + performancePrf = None, inputAsMF = False,# Fonction(s) as Multi-Functions scheduledBy = None, toBeChecked = False, @@ -444,6 +448,15 @@ class FullOperator(object): __Parameters["EnableMultiProcessingInEvaluation"] = False if "withIncrement" in __Parameters: # Temporaire __Parameters["DifferentialIncrement"] = __Parameters["withIncrement"] + # Le défaut est équivalent à "ReducedOverallRequirements" + __reduceM, __avoidRC = True, True + if performancePrf is not None: + if performancePrf == "ReducedAmountOfCalculation": + __reduceM, __avoidRC = False, True + elif performancePrf == "ReducedMemoryFootprint": + __reduceM, __avoidRC = True, False + elif performancePrf == "NoSavings": + __reduceM, __avoidRC = False, False # if asScript is not None: __Matrix, __Function = None, None @@ -512,7 +525,8 @@ class FullOperator(object): if "CenteredFiniteDifference" not in __Function: __Function["CenteredFiniteDifference"] = False if "DifferentialIncrement" not in __Function: __Function["DifferentialIncrement"] = 0.01 if "withdX" not in __Function: __Function["withdX"] = None - if "withAvoidingRedundancy" not in __Function: __Function["withAvoidingRedundancy"] = avoidRC + if "withReducingMemoryUse" not in __Function: __Function["withReducingMemoryUse"] = __reduceM + if "withAvoidingRedundancy" not in __Function: __Function["withAvoidingRedundancy"] = __avoidRC if "withToleranceInRedundancy" not in __Function: __Function["withToleranceInRedundancy"] = 1.e-18 if "withLenghtOfRedundancy" not in __Function: __Function["withLenghtOfRedundancy"] = -1 if "NumberOfProcesses" not in __Function: __Function["NumberOfProcesses"] = None @@ -525,6 +539,7 @@ class FullOperator(object): increment = __Function["DifferentialIncrement"], dX = __Function["withdX"], extraArguments = self.__extraArgs, + reducingMemoryUse = __Function["withReducingMemoryUse"], avoidingRedundancy = __Function["withAvoidingRedundancy"], toleranceInRedundancy = __Function["withToleranceInRedundancy"], lenghtOfRedundancy = __Function["withLenghtOfRedundancy"], @@ -532,20 +547,20 @@ class FullOperator(object): mpWorkers = __Function["NumberOfProcesses"], mfEnabled = __Function["withmfEnabled"], ) - self.__FO["Direct"] = Operator( name = self.__name, fromMethod = FDA.DirectOperator, avoidingRedundancy = avoidRC, inputAsMultiFunction = inputAsMF, extraArguments = self.__extraArgs, enableMultiProcess = __Parameters["EnableMultiProcessingInEvaluation"] ) - self.__FO["Tangent"] = Operator( name = self.__name+"Tangent", fromMethod = FDA.TangentOperator, avoidingRedundancy = avoidRC, inputAsMultiFunction = inputAsMF, extraArguments = self.__extraArgs ) - self.__FO["Adjoint"] = Operator( name = self.__name+"Adjoint", fromMethod = FDA.AdjointOperator, avoidingRedundancy = avoidRC, inputAsMultiFunction = inputAsMF, extraArguments = self.__extraArgs ) + self.__FO["Direct"] = Operator( name = self.__name, fromMethod = FDA.DirectOperator, reducingMemoryUse = __reduceM, avoidingRedundancy = __avoidRC, inputAsMultiFunction = inputAsMF, extraArguments = self.__extraArgs, enableMultiProcess = __Parameters["EnableMultiProcessingInEvaluation"] ) + self.__FO["Tangent"] = Operator( name = self.__name+"Tangent", fromMethod = FDA.TangentOperator, reducingMemoryUse = __reduceM, avoidingRedundancy = __avoidRC, inputAsMultiFunction = inputAsMF, extraArguments = self.__extraArgs ) + self.__FO["Adjoint"] = Operator( name = self.__name+"Adjoint", fromMethod = FDA.AdjointOperator, reducingMemoryUse = __reduceM, avoidingRedundancy = __avoidRC, inputAsMultiFunction = inputAsMF, extraArguments = self.__extraArgs ) elif isinstance(__Function, dict) and \ ("Direct" in __Function) and ("Tangent" in __Function) and ("Adjoint" in __Function) and \ (__Function["Direct"] is not None) and (__Function["Tangent"] is not None) and (__Function["Adjoint"] is not None): - self.__FO["Direct"] = Operator( name = self.__name, fromMethod = __Function["Direct"], avoidingRedundancy = avoidRC, inputAsMultiFunction = inputAsMF, extraArguments = self.__extraArgs, enableMultiProcess = __Parameters["EnableMultiProcessingInEvaluation"] ) - self.__FO["Tangent"] = Operator( name = self.__name+"Tangent", fromMethod = __Function["Tangent"], avoidingRedundancy = avoidRC, inputAsMultiFunction = inputAsMF, extraArguments = self.__extraArgs ) - self.__FO["Adjoint"] = Operator( name = self.__name+"Adjoint", fromMethod = __Function["Adjoint"], avoidingRedundancy = avoidRC, inputAsMultiFunction = inputAsMF, extraArguments = self.__extraArgs ) + self.__FO["Direct"] = Operator( name = self.__name, fromMethod = __Function["Direct"], reducingMemoryUse = __reduceM, avoidingRedundancy = __avoidRC, inputAsMultiFunction = inputAsMF, extraArguments = self.__extraArgs, enableMultiProcess = __Parameters["EnableMultiProcessingInEvaluation"] ) + self.__FO["Tangent"] = Operator( name = self.__name+"Tangent", fromMethod = __Function["Tangent"], reducingMemoryUse = __reduceM, avoidingRedundancy = __avoidRC, inputAsMultiFunction = inputAsMF, extraArguments = self.__extraArgs ) + self.__FO["Adjoint"] = Operator( name = self.__name+"Adjoint", fromMethod = __Function["Adjoint"], reducingMemoryUse = __reduceM, avoidingRedundancy = __avoidRC, inputAsMultiFunction = inputAsMF, extraArguments = self.__extraArgs ) elif asMatrix is not None: __matrice = numpy.matrix( __Matrix, numpy.float ) - self.__FO["Direct"] = Operator( name = self.__name, fromMatrix = __matrice, avoidingRedundancy = avoidRC, inputAsMultiFunction = inputAsMF, enableMultiProcess = __Parameters["EnableMultiProcessingInEvaluation"] ) - self.__FO["Tangent"] = Operator( name = self.__name+"Tangent", fromMatrix = __matrice, avoidingRedundancy = avoidRC, inputAsMultiFunction = inputAsMF ) - self.__FO["Adjoint"] = Operator( name = self.__name+"Adjoint", fromMatrix = __matrice.T, avoidingRedundancy = avoidRC, inputAsMultiFunction = inputAsMF ) + self.__FO["Direct"] = Operator( name = self.__name, fromMatrix = __matrice, reducingMemoryUse = __reduceM, avoidingRedundancy = __avoidRC, inputAsMultiFunction = inputAsMF, enableMultiProcess = __Parameters["EnableMultiProcessingInEvaluation"] ) + self.__FO["Tangent"] = Operator( name = self.__name+"Tangent", fromMatrix = __matrice, reducingMemoryUse = __reduceM, avoidingRedundancy = __avoidRC, inputAsMultiFunction = inputAsMF ) + self.__FO["Adjoint"] = Operator( name = self.__name+"Adjoint", fromMatrix = __matrice.T, reducingMemoryUse = __reduceM, avoidingRedundancy = __avoidRC, inputAsMultiFunction = inputAsMF ) del __matrice else: raise ValueError("The %s object is improperly defined or undefined, it requires at minima either a matrix, a Direct operator for approximate derivatives or a Tangent/Adjoint operators pair. Please check your operator input."%self.__name) @@ -1010,7 +1025,10 @@ class Algorithm(object): self._parameters[k] = self.setParameterValue(k) else: pass - logging.debug("%s %s : %s", self._name, self.__required_parameters[k]["message"], self._parameters[k]) + if hasattr(self._parameters[k],"__len__") and len(self._parameters[k]) > 100: + logging.debug("%s %s de longueur %s", self._name, self.__required_parameters[k]["message"], len(self._parameters[k])) + else: + logging.debug("%s %s : %s", self._name, self.__required_parameters[k]["message"], self._parameters[k]) def _setInternalState(self, key=None, value=None, fromDico={}, reset=False): """ diff --git a/src/daComposant/daCore/Interfaces.py b/src/daComposant/daCore/Interfaces.py index 3a6895c..6d301ae 100644 --- a/src/daComposant/daCore/Interfaces.py +++ b/src/daComposant/daCore/Interfaces.py @@ -132,7 +132,7 @@ class _TUIViewer(GenericCaseViewer): if k == "ColMajor" and not __v: continue if k == "InputFunctionAsMulti" and not __v: continue if k == "nextStep" and not __v: continue - if k == "AvoidRC" and __v: continue + if k == "PerformanceProfile" and __v: continue if k == "noDetails": continue if isinstance(__v,Persistence.Persistence): __v = __v.values() if callable(__v): __text = self._missing%__v.__name__+__text @@ -491,7 +491,7 @@ class _SCDViewer(GenericCaseViewer): elif __k in ('Stored', 'Checked', 'ColMajor', 'InputFunctionAsMulti', 'nextStep'): if bool(__v): __text += "%s_config['%s'] = '%s'\n"%(__command,__k,int(bool(__v))) - elif __k in ('AvoidRC', 'noDetails'): + elif __k in ('PerformanceProfile', 'noDetails'): if not bool(__v): __text += "%s_config['%s'] = '%s'\n"%(__command,__k,int(bool(__v))) else: @@ -646,7 +646,7 @@ class _ReportViewer(GenericCaseViewer): if k == "ColMajor" and not __v: continue if k == "InputFunctionAsMulti" and not __v: continue if k == "nextStep" and not __v: continue - if k == "AvoidRC" and __v: continue + if k == "PerformanceProfile" and __v: continue if k == "noDetails": continue if k == "Concept": continue if k == "self": continue diff --git a/src/daComposant/daCore/NumericObjects.py b/src/daComposant/daCore/NumericObjects.py index 1a34b32..58d723e 100644 --- a/src/daComposant/daCore/NumericObjects.py +++ b/src/daComposant/daCore/NumericObjects.py @@ -66,6 +66,7 @@ class FDApproximation(object): increment = 0.01, dX = None, extraArguments = None, + reducingMemoryUse = False, avoidingRedundancy = True, toleranceInRedundancy = 1.e-18, lenghtOfRedundancy = -1, @@ -75,6 +76,7 @@ class FDApproximation(object): ): self.__name = str(name) self.__extraArgs = extraArguments + # if mpEnabled: try: import multiprocessing @@ -88,12 +90,12 @@ class FDApproximation(object): self.__mpWorkers = None logging.debug("FDA Calculs en multiprocessing : %s (nombre de processus : %s)"%(self.__mpEnabled,self.__mpWorkers)) # - if mfEnabled: - self.__mfEnabled = True - else: - self.__mfEnabled = False + self.__mfEnabled = bool(mfEnabled) logging.debug("FDA Calculs en multifonctions : %s"%(self.__mfEnabled,)) # + self.__rmEnabled = bool(reducingMemoryUse) + logging.debug("FDA Calculs avec réduction mémoire : %s"%(self.__rmEnabled,)) + # if avoidingRedundancy: self.__avoidRC = True self.__tolerBP = float(toleranceInRedundancy) @@ -105,6 +107,9 @@ class FDApproximation(object): self.__listJPIN = [] # Jacobian Previous Calculated Increment Norms else: self.__avoidRC = False + logging.debug("FDA Calculs avec réduction des doublons : %s"%self.__avoidRC) + if self.__avoidRC: + logging.debug("FDA Tolérance de détermination des doublons : %.2e"%self.__tolerBP) # if self.__mpEnabled: if isinstance(Function,types.FunctionType): @@ -150,9 +155,6 @@ class FDApproximation(object): self.__dX = None else: self.__dX = numpy.ravel( dX ) - 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=None): @@ -164,6 +166,29 @@ class FDApproximation(object): break return __ac, __iac + # --------------------------------------------------------- + def __listdotwith__(self, __LMatrix, __dotWith = None, __dotTWith = None): + "Produit incrémental d'une matrice liste de colonnes avec un vecteur" + if not isinstance(__LMatrix, (list,tuple)): + raise TypeError("Columnwise list matrix has not the proper type: %s"%type(__LMatrix)) + if __dotWith is not None: + __Idwx = numpy.ravel( __dotWith ) + assert len(__LMatrix) == __Idwx.size, "Incorrect size of elements" + __Produit = numpy.zeros(__LMatrix[0].size) + for i, col in enumerate(__LMatrix): + __Produit += float(__Idwx[i]) * col + return __Produit + elif __dotTWith is not None: + _Idwy = numpy.ravel( __dotTWith ).T + assert __LMatrix[0].size == _Idwy.size, "Incorrect size of elements" + __Produit = numpy.zeros(len(__LMatrix)) + for i, col in enumerate(__LMatrix): + __Produit[i] = float( _Idwy @ col) + return __Produit + else: + __Produit = None + return __Produit + # --------------------------------------------------------- def DirectOperator(self, X, **extraArgs ): """ @@ -181,7 +206,7 @@ class FDApproximation(object): return _HX # --------------------------------------------------------- - def TangentMatrix(self, X ): + def TangentMatrix(self, X, dotWith = None, dotTWith = None ): """ 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 @@ -240,6 +265,11 @@ class FDApproximation(object): if __alreadyCalculated: logging.debug("FDA Calcul Jacobienne (par récupération du doublon %i)"%__i) _Jacobienne = self.__listJPCR[__i] + logging.debug("FDA Fin du calcul de la Jacobienne") + if dotWith is not None: + return numpy.dot(_Jacobienne, numpy.ravel( dotWith )) + elif dotTWith is not None: + return numpy.dot(_Jacobienne.T, numpy.ravel( dotTWith )) else: logging.debug("FDA Calcul Jacobienne (explicite)") if self.__centeredDF: @@ -359,24 +389,29 @@ class FDApproximation(object): _HX_plus_dXi = self.DirectOperator( _X_plus_dXi ) # _Jacobienne.append( numpy.ravel(( _HX_plus_dXi - _HX ) / _dXi) ) - # # - _Jacobienne = numpy.transpose( numpy.vstack( _Jacobienne ) ) - if self.__avoidRC: - if self.__lenghtRJ < 0: self.__lenghtRJ = 2 * _X.size - while len(self.__listJPCP) > 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) ) - # - logging.debug("FDA Fin du calcul de la Jacobienne") + if (dotWith is not None) or (dotTWith is not None): + __Produit = self.__listdotwith__(_Jacobienne, dotWith, dotTWith) + else: + __Produit = None + if __Produit is None or self.__avoidRC: + _Jacobienne = numpy.transpose( numpy.vstack( _Jacobienne ) ) + if self.__avoidRC: + if self.__lenghtRJ < 0: self.__lenghtRJ = 2 * _X.size + while len(self.__listJPCP) > 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) ) + logging.debug("FDA Fin du calcul de la Jacobienne") + if __Produit is not None: + return __Produit # return _Jacobienne @@ -389,26 +424,25 @@ class FDApproximation(object): ne doivent pas être données ici à la fonction utilisateur. """ if self.__mfEnabled: - assert len(paire) == 1, "Incorrect lenght of arguments" + assert len(paire) == 1, "Incorrect length of arguments" _paire = paire[0] assert len(_paire) == 2, "Incorrect number of arguments" else: assert len(paire) == 2, "Incorrect number of arguments" _paire = paire X, dX = _paire - _Jacobienne = self.TangentMatrix( X ) if dX is None or len(dX) == 0: # # Calcul de la forme matricielle si le second argument est None # ------------------------------------------------------------- + _Jacobienne = self.TangentMatrix( X ) if self.__mfEnabled: return [_Jacobienne,] else: return _Jacobienne else: # # Calcul de la valeur linéarisée de H en X appliqué à dX # ------------------------------------------------------ - _dX = numpy.ravel( dX ) - _HtX = numpy.dot(_Jacobienne, _dX) + _HtX = self.TangentMatrix( X, dotWith = dX ) if self.__mfEnabled: return [_HtX,] else: return _HtX @@ -421,26 +455,25 @@ class FDApproximation(object): ne doivent pas être données ici à la fonction utilisateur. """ if self.__mfEnabled: - assert len(paire) == 1, "Incorrect lenght of arguments" + assert len(paire) == 1, "Incorrect length of arguments" _paire = paire[0] assert len(_paire) == 2, "Incorrect number of arguments" else: assert len(paire) == 2, "Incorrect number of arguments" _paire = paire X, Y = _paire - _JacobienneT = self.TangentMatrix( X ).T if Y is None or len(Y) == 0: # # Calcul de la forme matricielle si le second argument est None # ------------------------------------------------------------- + _JacobienneT = self.TangentMatrix( X ).T if self.__mfEnabled: return [_JacobienneT,] else: return _JacobienneT else: # # Calcul de la valeur de l'adjoint en X appliqué à Y # -------------------------------------------------- - _Y = numpy.ravel( Y ) - _HaY = numpy.dot(_JacobienneT, _Y) + _HaY = self.TangentMatrix( X, dotTWith = Y ) if self.__mfEnabled: return [_HaY,] else: return _HaY -- 2.39.2