From 7bf2ba7defcc58ecaa43a0beece56ca63c779534 Mon Sep 17 00:00:00 2001 From: Jean-Philippe ARGAUD Date: Tue, 25 Apr 2023 11:16:48 +0200 Subject: [PATCH] Code review and update for PSO --- src/daComposant/daAlgorithms/Atoms/ecwnpso.py | 31 ++++++++----------- src/daComposant/daAlgorithms/Atoms/ecwopso.py | 24 +++++++------- src/daComposant/daCore/Interfaces.py | 2 ++ src/daComposant/daCore/NumericObjects.py | 14 +++++++++ 4 files changed, 40 insertions(+), 31 deletions(-) diff --git a/src/daComposant/daAlgorithms/Atoms/ecwnpso.py b/src/daComposant/daAlgorithms/Atoms/ecwnpso.py index ef7c9d3..7fc546a 100644 --- a/src/daComposant/daAlgorithms/Atoms/ecwnpso.py +++ b/src/daComposant/daAlgorithms/Atoms/ecwnpso.py @@ -21,27 +21,16 @@ # Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D __doc__ = """ - Standard Particle Swarm Optimization + Canonical Particle Swarm Optimization """ __author__ = "Jean-Philippe ARGAUD" import numpy, logging, copy -from daCore.NumericObjects import ApplyBounds, ForceNumericBounds +from daCore.NumericObjects import ApplyBounds, VariablesAndIncrementsBounds from numpy.random import uniform as rand # ============================================================================== def ecwnpso(selfA, Xb, Y, HO, R, B): - # - if ("BoxBounds" in selfA._parameters) and isinstance(selfA._parameters["BoxBounds"], (list, tuple)) and (len(selfA._parameters["BoxBounds"]) > 0): - BoxBounds = selfA._parameters["BoxBounds"] - logging.debug("%s Prise en compte des bornes d'incréments de paramètres effectuée"%(selfA._name,)) - else: - raise ValueError("Particle Swarm Optimization requires bounds on all variables increments to be truly given (BoxBounds).") - BoxBounds = numpy.array(BoxBounds) - if numpy.isnan(BoxBounds).any(): - raise ValueError("Particle Swarm Optimization requires bounds on all variables increments to be truly given (BoxBounds), \"None\" is not allowed. The actual increments bounds are:\n%s"%BoxBounds) - # - selfA._parameters["Bounds"] = ForceNumericBounds( selfA._parameters["Bounds"] ) # Hm = HO["Direct"].appliedTo # @@ -50,6 +39,13 @@ def ecwnpso(selfA, Xb, Y, HO, R, B): # Xini = selfA._parameters["InitializationPoint"] # + Bounds, BoxBounds = VariablesAndIncrementsBounds( + selfA._parameters["Bounds"], + selfA._parameters["BoxBounds"], + Xini, + selfA._name, + ) + # def CostFunction(x, QualityMeasure="AugmentedWeightedLeastSquares"): _X = numpy.asarray( x ).reshape((-1,1)) _HX = numpy.asarray( Hm( _X ) ).reshape((-1,1)) @@ -104,16 +100,15 @@ def ecwnpso(selfA, Xb, Y, HO, R, B): # # Initialisation de l'essaim # -------------------------- - LimitStep = Xini.reshape((-1,1)) + BoxBounds - __dx = __vc * numpy.abs(BoxBounds[:,1]-BoxBounds[:,0]) - LimitSpeed = numpy.vstack((-__dx,__dx)).T + LimitPlace = Bounds + LimitSpeed = 0.5 * BoxBounds # "1/2*([Xmin,Xmax]-Xini)" # NumberOfFunctionEvaluations = 1 JXini, JbXini, JoXini = CostFunction(Xini,selfA._parameters["QualityCriterion"]) # Swarm = numpy.zeros((__nbI,3,__nbP)) # 3 car (x,v,xbest) for __p in range(__nbP) : - Swarm[:,0,__p] = rand( low=LimitStep[__p,0], high=LimitStep[__p,1], size=__nbI) # Position + Swarm[:,0,__p] = rand( low=LimitPlace[__p,0], high=LimitPlace[__p,1], size=__nbI) # Position Swarm[:,1,__p] = rand( low=LimitSpeed[__p,0], high=LimitSpeed[__p,1], size=__nbI) # Velocity logging.debug("%s Initialisation of the swarm with %i insects of size %i "%(selfA._name,Swarm.shape[0],Swarm.shape[2])) # @@ -154,7 +149,7 @@ def ecwnpso(selfA, Xb, Y, HO, R, B): Swarm[__i,1,:] = ApplyBounds( __velins, LimitSpeed ) # Position __velins = Swarm[__i,0,:] + Swarm[__i,1,:] - Swarm[__i,0,:] = ApplyBounds( __velins, selfA._parameters["Bounds"] ) + Swarm[__i,0,:] = ApplyBounds( __velins, LimitPlace ) # NumberOfFunctionEvaluations += 1 JTest, JbTest, JoTest = CostFunction(Swarm[__i,0,:],selfA._parameters["QualityCriterion"]) diff --git a/src/daComposant/daAlgorithms/Atoms/ecwopso.py b/src/daComposant/daAlgorithms/Atoms/ecwopso.py index 00e155f..a1c7d16 100644 --- a/src/daComposant/daAlgorithms/Atoms/ecwopso.py +++ b/src/daComposant/daAlgorithms/Atoms/ecwopso.py @@ -26,20 +26,12 @@ __doc__ = """ __author__ = "Jean-Philippe ARGAUD" import numpy, logging, copy +from daCore.NumericObjects import VariablesAndIncrementsBounds from numpy.random import uniform as rand # ============================================================================== def ecwopso(selfA, Xb, Y, HO, R, B): # - if ("BoxBounds" in selfA._parameters) and isinstance(selfA._parameters["BoxBounds"], (list, tuple)) and (len(selfA._parameters["BoxBounds"]) > 0): - BoxBounds = selfA._parameters["BoxBounds"] - logging.debug("%s Prise en compte des bornes d'incréments de paramètres effectuée"%(selfA._name,)) - else: - raise ValueError("Particle Swarm Optimization requires bounds on all variables increments to be truly given (BoxBounds).") - BoxBounds = numpy.array(BoxBounds) - if numpy.isnan(BoxBounds).any(): - raise ValueError("Particle Swarm Optimization requires bounds on all variables increments to be truly given (BoxBounds), \"None\" is not allowed. The actual increments bounds are:\n%s"%BoxBounds) - # Hm = HO["Direct"].appliedTo # BI = B.getI() @@ -47,6 +39,13 @@ def ecwopso(selfA, Xb, Y, HO, R, B): # Xini = selfA._parameters["InitializationPoint"] # + Bounds, BoxBounds = VariablesAndIncrementsBounds( + selfA._parameters["Bounds"], + selfA._parameters["BoxBounds"], + Xini, + selfA._name, + ) + # def CostFunction(x, QualityMeasure="AugmentedWeightedLeastSquares"): _X = numpy.asarray( x ).reshape((-1,1)) _HX = numpy.asarray( Hm( _X ) ).reshape((-1,1)) @@ -101,16 +100,15 @@ def ecwopso(selfA, Xb, Y, HO, R, B): # # Initialisation de l'essaim # -------------------------- - LimitStep = Xini.reshape((-1,1)) + BoxBounds - __dx = __vc * numpy.abs(BoxBounds[:,1]-BoxBounds[:,0]) - LimitSpeed = numpy.vstack((-__dx,__dx)).T + LimitPlace = Bounds + LimitSpeed = 0.5 * BoxBounds # "1/2*([Xmin,Xmax]-Xini)" # NumberOfFunctionEvaluations = 1 JXini, JbXini, JoXini = CostFunction(Xini,selfA._parameters["QualityCriterion"]) # Swarm = numpy.zeros((__nbI,3,__nbP)) # 3 car (x,v,xbest) for __p in range(__nbP) : - Swarm[:,0,__p] = rand( low=LimitStep[__p,0], high=LimitStep[__p,1], size=__nbI) # Position + Swarm[:,0,__p] = rand( low=LimitPlace[__p,0], high=LimitPlace[__p,1], size=__nbI) # Position Swarm[:,1,__p] = rand( low=LimitSpeed[__p,0], high=LimitSpeed[__p,1], size=__nbI) # Velocity logging.debug("%s Initialisation of the swarm with %i insects of size %i "%(selfA._name,Swarm.shape[0],Swarm.shape[2])) # diff --git a/src/daComposant/daCore/Interfaces.py b/src/daComposant/daCore/Interfaces.py index 5c2d66b..0dbfbf4 100644 --- a/src/daComposant/daCore/Interfaces.py +++ b/src/daComposant/daCore/Interfaces.py @@ -296,6 +296,8 @@ class _COMViewer(GenericCaseViewer): __Dict.pop('Algorithm','') __Dict.pop('Parameters','') if 'SetSeed' in __Dict:__Dict['SetSeed'] = int(__Dict['SetSeed']) + if 'Bounds' in __Dict and type(__Dict['Bounds']) is str: + __Dict['Bounds'] = eval(__Dict['Bounds']) if 'BoxBounds' in __Dict and type(__Dict['BoxBounds']) is str: __Dict['BoxBounds'] = eval(__Dict['BoxBounds']) if len(__Dict) > 0: diff --git a/src/daComposant/daCore/NumericObjects.py b/src/daComposant/daCore/NumericObjects.py index ee25868..2f14503 100644 --- a/src/daComposant/daCore/NumericObjects.py +++ b/src/daComposant/daCore/NumericObjects.py @@ -895,6 +895,20 @@ def ApplyBounds( __Vector, __Bounds, __newClip = True ): # return __Vector +# ============================================================================== +def VariablesAndIncrementsBounds( __Bounds, __BoxBounds, __Xini, __Name, __Multiplier = 1. ): + __Bounds = ForceNumericBounds( __Bounds ) + __BoxBounds = ForceNumericBounds( __BoxBounds ) + if __Bounds is None and __BoxBounds is None: + raise ValueError("Algorithm %s requires bounds on all variables (by Bounds), or on all variables increments (by BoxBounds), or both, to be explicitly given."%(__Name,)) + elif __Bounds is None and __BoxBounds is not None: + __Bounds = __BoxBounds + logging.debug("%s Définition des bornes de paramètres à partir des bornes d'incréments courantes"%(__Name,)) + elif __Bounds is not None and __BoxBounds is None: + __BoxBounds = __Multiplier * (__Bounds - __Xini.reshape((-1,1))) # "M * [Xmin,Xmax]-Xini" + logging.debug("%s Définition des bornes d'incréments de paramètres à partir des bornes courantes"%(__Name,)) + return __Bounds, __BoxBounds + # ============================================================================== def Apply3DVarRecentringOnEnsemble( __EnXn, __EnXf, __Ynpu, __HO, __R, __B, __SuppPars ): "Recentre l'ensemble Xn autour de l'analyse 3DVAR" -- 2.39.2