-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathPSO_python.py
1033 lines (860 loc) · 41.3 KB
/
PSO_python.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
################################################################################
# CÓDIGO OPTIMIZACIÓN CON ALGORITMO PSO #
# #
# This work by Joaquín Amat Rodrigo is licensed under a Creative Commons #
# Attribution 4.0 International License. #
################################################################################
# coding=utf-8
#%%
################################################################################
# LIBRERÍAS NECESARIAS #
################################################################################
import numpy as np
import random
import warnings
import random
import copy
import pandas as pd
import time
from datetime import datetime
#%%
################################################################################
# CLASE PARTÍCULA #
################################################################################
class Particula:
"""
Esta clase representa nueva partícula con una posición inicial definida por
una combinación de valores numéricos aleatorios y velocidad de 0. El rango
de posibles valores para cada variable (posición) puede estar acotado. Al
crear una nueva partícula, solo se dispone de información sobre su posición
inicial y velocidad, el resto de atributos están vacíos.
Parameters
----------
n_variables : `int`
número de variables que definen la posición de la partícula.
limites_inf : `list` or `numpy.ndarray`, optional
límite inferior de cada variable. Si solo se quiere predefinir límites
de alguna variable, emplear ``None``. Los ``None`` serán remplazados
por el valor (-10**3). (default is ``None``)
limites_sup : `list` or `numpy.ndarray`, optional
límite superior de cada variable. Si solo se quiere predefinir límites
de alguna variable, emplear ``None``. Los ``None`` serán remplazados
por el valor (+10**3). (default is ``None``)
verbose : `bool`, optional
mostrar información de la partícula creada. (default is ``False``)
Attributes
----------
n_variables : `int`
número de variables que definen la posición de la partícula.
limites_inf : `list` or `numpy.ndarray`
límite inferior de cada variable. Si solo se quiere predefinir límites
de alguna variable, emplear ``None``. Los ``None`` serán remplazados por
el valor (-10**3).
limites_sup : `list` or `numpy.ndarray`
límite superior de cada variable. Si solo se quiere predefinir límites
de alguna variable, emplear ``None``. Los``None`` serán remplazados por
el valor (+10**3).
mejor_valor : `numpy.ndarray`
mejor valor que ha tenido la partícula hasta el momento.
mejor_posicion : `numpy.ndarray`
posición en la que la partícula ha tenido el mejor valor hasta el momento.
valor : `float`
valor actual de la partícula. Resultado de evaluar la función objetivo
con la posición actual.
velocidad : `numpy.ndarray`
array con la velocidad actual de la partícula.
posicion : `numpy.ndarray`
posición actual de la partícula.
Raises
------
raise Exception
si `limites_inf` es distinto de None y su longitud no coincide con
`n_variables`.
raise Exception
si `limites_sup` es distinto de None y su longitud no coincide con
`n_variables`.
Examples
--------
Ejemplo creación partícula.
>>> part = Particula(
n_variables = 3,
limites_inf = [-1,2,0],
limites_sup = [4,10,20],
verbose = True
)
"""
def __init__(self, n_variables, limites_inf=None, limites_sup=None,
verbose=False):
# Número de variables de la partícula
self.n_variables = n_variables
# Límite inferior de cada variable
self.limites_inf = limites_inf
# Límite superior de cada variable
self.limites_sup = limites_sup
# Posición de la partícula
self.posicion = np.repeat(None, n_variables)
# Velocidad de la parícula
self.velocidad = np.repeat(None, n_variables)
# Valor de la partícula
self.valor = np.repeat(None, 1)
# Mejor valor que ha tenido la partícula hasta el momento
self.mejor_valor = None
# Mejor posición en la que ha estado la partícula hasta el momento
self.mejor_posicion = None
# CONVERSIONES DE TIPO INICIALES
# ----------------------------------------------------------------------
# Si limites_inf o limites_sup no son un array numpy, se convierten en
# ello.
if self.limites_inf is not None \
and not isinstance(self.limites_inf,np.ndarray):
self.limites_inf = np.array(self.limites_inf)
if self.limites_sup is not None \
and not isinstance(self.limites_sup,np.ndarray):
self.limites_sup = np.array(self.limites_sup)
# COMPROBACIONES INICIALES: EXCEPTIONS Y WARNINGS
# ----------------------------------------------------------------------
if self.limites_inf is not None \
and len(self.limites_inf) != self.n_variables:
raise Exception(
"limites_inf debe tener un valor por cada variable. " +
"Si para alguna variable no se quiere límite, emplear None. " +
"Ejemplo: limites_inf = [10, None, 5]"
)
elif self.limites_sup is not None \
and len(self.limites_sup) != self.n_variables:
raise Exception(
"limites_sup debe tener un valor por cada variable. " +
"Si para alguna variable no se quiere límite, emplear None. " +
"Ejemplo: limites_sup = [10, None, 5]"
)
elif (self.limites_inf is None) or (self.limites_sup is None):
warnings.warn(
"Es altamente recomendable indicar los límites dentro de los " +
"cuales debe buscarse la solución de cada variable. " +
"Por defecto se emplea [-10^3, 10^3]."
)
elif any(np.concatenate((self.limites_inf, self.limites_sup)) == None):
warnings.warn(
"Los límites empleados por defecto cuando no se han definido " +
"son: [-10^3, 10^3]."
)
# COMPROBACIONES INICIALES: ACCIONES
# ----------------------------------------------------------------------
# Si no se especifica limites_inf, el valor mínimo que pueden tomar las
# variables es -10^3.
if self.limites_inf is None:
self.limites_inf = np.repeat(-10**3, self.n_variables)
# Si no se especifica limites_sup, el valor máximo que pueden tomar las
# variables es 10^3.
if self.limites_sup is None:
self.limites_sup = np.repeat(+10**3, self.n_variables)
# Si los límites no son nulos, se reemplazan aquellas posiciones None por
# el valor por defecto -10^3 y 10^3.
if self.limites_inf is not None:
self.limites_inf[self.limites_inf == None] = -10**3
if self.limites_sup is not None:
self.limites_sup[self.limites_sup == None] = +10**3
# BUCLE PARA ASIGNAR UN VALOR A CADA UNA DE LAS VARIABLES QUE DEFINEN LA
# POSICIÓN
# ----------------------------------------------------------------------
for i in np.arange(self.n_variables):
# Para cada posición, se genera un valor aleatorio dentro del rango
# permitido para esa variable.
self.posicion[i] = random.uniform(
self.limites_inf[i],
self.limites_sup[i]
)
# LA VELOCIDAD INICIAL DE LA PARTÍCULA ES 0
# ----------------------------------------------------------------------
self.velocidad = np.repeat(0, self.n_variables)
# INFORMACIÓN DEL PROCESO (VERBOSE)
# ----------------------------------------------------------------------
if verbose:
print("Nueva partícula creada")
print("----------------------")
print("Posición: " + str(self.posicion))
print("Límites inferiores de cada variable: " \
+ str(self.limites_inf))
print("Límites superiores de cada variable: " \
+ str(self.limites_sup))
print("Velocidad: " + str(self.velocidad))
print("")
def __repr__(self):
"""
Información que se muestra cuando se imprime un objeto partícula.
"""
texto = "Partícula" \
+ "\n" \
+ "---------" \
+ "\n" \
+ "Posición: " + str(self.posicion) \
+ "\n" \
+ "Velocidad: " + str(self.velocidad) \
+ "\n" \
+ "Mejor posicion: " + str(self.mejor_posicion) \
+ "\n" \
+ "Mejor valor: " + str(self.mejor_valor) \
+ "\n" \
+ "Límites inferiores de cada variable: " \
+ str(self.limites_inf) \
+ "\n" \
+ "Límites superiores de cada variable: " \
+ str(self.limites_sup) \
+ "\n"
return(texto)
def evaluar_particula(self, funcion_objetivo, optimizacion, verbose = False):
"""
Este método evalúa una partícula calculando el valor que toma la función
objetivo en la posición en la que se encuentra. Además, compara si la
nueva posición es mejor que las anteriores. Modifica los atributos
valor, mejor_valor y mejor_posicion de la partícula.
Parameters
----------
funcion_objetivo : `function`
función que se quiere optimizar.
optimizacion : {'maximizar', 'minimizar'}
dependiendo de esto, el mejor valor histórico de la partícula será
el mayor o el menor valor que ha tenido hasta el momento.
verbose : `bool`, optional
mostrar información del proceso por pantalla. (default is ``False``)
Raises
------
raise Exception
si el argumento `optimizacion` es distinto de 'maximizar' o
'minimizar'.
Examples
--------
Ejemplo evaluar partícula con una función objetivo.
>>> part = Particula(
n_variables = 3,
limites_inf = [-1,2,0],
limites_sup = [4,10,20],
verbose = True
)
>>> def funcion_objetivo(x_0, x_1, x_2):
f= x_0**2 + x_1**2 + x_2**2
return(f)
>>> part.evaluar_particula(
funcion_objetivo = funcion_objetivo,
optimizacion = "maximizar",
verbose = True
)
"""
# COMPROBACIONES INICIALES: EXCEPTIONS Y WARNINGS
# ----------------------------------------------------------------------
if not optimizacion in ["maximizar", "minimizar"]:
raise Exception(
"El argumento optimizacion debe ser: 'maximizar' o 'minimizar'"
)
# EVALUACIÓN DE LA FUNCIÓN OBJETIVO EN LA POSICIÓN ACTUAL
# ----------------------------------------------------------------------
self.valor = funcion_objetivo(*self.posicion)
# MEJOR VALOR Y POSICIÓN
# ----------------------------------------------------------------------
# Se compara el valor actual con el mejor valor histórico. La comparación
# es distinta dependiendo de si se desea maximizar o minimizar.
# Si no existe ningún valor histórico, se almacena el actual. Si ya
# existe algún valor histórico se compara con el actual y, de ser mejor
# este último, se sobrescribe.
if self.mejor_valor is None:
self.mejor_valor = np.copy(self.valor)
self.mejor_posicion = np.copy(self.posicion)
else:
if optimizacion == "minimizar":
if self.valor < self.mejor_valor:
self.mejor_valor = np.copy(self.valor)
self.mejor_posicion = np.copy(self.posicion)
else:
if self.valor > self.mejor_valor:
self.mejor_valor = np.copy(self.valor)
self.mejor_posicion = np.copy(self.posicion)
# INFORMACIÓN DEL PROCESO (VERBOSE)
# ----------------------------------------------------------------------
if verbose:
print("La partícula ha sido evaluada")
print("-----------------------------")
print("Valor actual: " + str(self.valor))
print("")
def mover_particula(self, mejor_p_enjambre, inercia=0.8, peso_cognitivo=2,
peso_social=2, verbose=False):
"""
Este método ejecuta el movimiento de una partícula, lo que implica
actualizar su velocidad y posición. No se permite que la partícula
salga de la zona de búsqueda acotada por los límites.
Parameters
----------
mejor_p_enjambre : `np.narray`
mejor posición de todo el enjambre.
inercia : `float`, optional
coeficiente de inercia. (default is 0.8)
peso_cognitivo : `float`, optional
coeficiente cognitivo. (default is 2)
peso_social : `float`, optional
coeficiente social. (default is 2)
verbose : `bool`, optional
mostrar información del proceso por pantalla. (default is ``False``)
Examples
--------
Ejemplo mover partícula.
>>> part = Particula(
n_variables = 3,
limites_inf = [-1,2,0],
limites_sup = [4,10,20],
verbose = True
)
>>> def funcion_objetivo(x_0, x_1, x_2):
f= x_0**2 + x_1**2 + x_2**2
return(f)
>>> part.evaluar_particula(
funcion_objetivo = funcion_objetivo,
optimizacion = "maximizar",
verbose = True
)
>>> part.mover_particula(
mejor_p_enjambre = np.array([-1000,-1000,+1000]),
inercia = 0.8,
peso_cognitivo = 2,
peso_social = 2,
verbose = True
)
"""
# ACTUALIZACIÓN DE LA VELOCIDAD
# ----------------------------------------------------------------------
componente_velocidad = inercia * self.velocidad
r1 = np.random.uniform(low=0.0, high=1.0, size = len(self.velocidad))
r2 = np.random.uniform(low=0.0, high=1.0, size = len(self.velocidad))
componente_cognitivo = peso_cognitivo * r1 * (self.mejor_posicion \
- self.posicion)
componente_social = peso_social * r2 * (mejor_p_enjambre \
- self.posicion)
nueva_velocidad = componente_velocidad + componente_cognitivo \
+ componente_social
self.velocidad = np.copy(nueva_velocidad)
# ACTUALIZACIÓN DE LA POSICIÓN
# ----------------------------------------------------------------------
self.posicion = self.posicion + self.velocidad
# COMPROBAR LÍMITES
# ----------------------------------------------------------------------
# Se comprueba si algún valor de la nueva posición supera los límites
# impuestos. En tal caso, se sobrescribe con el valor del límite
# correspondiente y se reinicia a 0 la velocidad de la partícula en esa
# componente.
for i in np.arange(len(self.posicion)):
if self.posicion[i] < self.limites_inf[i]:
self.posicion[i] = self.limites_inf[i]
self.velocidad[i] = 0
if self.posicion[i] > self.limites_sup[i]:
self.posicion[i] = self.limites_sup[i]
self.velocidad[i] = 0
# INFORMACIÓN DEL PROCESO (VERBOSE)
# ----------------------------------------------------------------------
if verbose:
print("La partícula se ha desplazado")
print("-----------------------------")
print("Nueva posición: " + str(self.posicion))
print("")
#%%
################################################################################
# CLASE ENJAMBRE (SWARM) #
################################################################################
class Enjambre:
"""
Esta clase crea un enjambre de n partículas.
Parameters
----------
n_particulas :`int`
número de partículas del enjambre.
n_variables : `int`
número de variables que definen la posición de las partícula.
limites_inf : `list` or `numpy.ndarray`
límite inferior de cada variable. Si solo se quiere predefinir límites
de alguna variable, emplear ``None``. Los ``None`` serán remplazados por
el valor (-10**3).
limites_sup : `list` or `numpy.ndarray`
límite superior de cada variable. Si solo se quiere predefinir límites
de alguna variable, emplear ``None``. Los``None`` serán remplazados por
el valor (+10**3).
verbose : `bool`, optional
mostrar información del proceso por pantalla. (default is ``False``)
Attributes
----------
partículas : `list`
lista con todas las partículas del enjambre.
n_particulas :`int`
número de partículas del enjambre.
n_variables : `int`
número de variables que definen la posición de las partícula.
limites_inf : `list` or `numpy.ndarray`
límite inferior de cada variable.
limites_sup : `list` or `numpy.ndarray`
límite superior de cada variable.
mejor_particula : `object particula`
la mejor partícula del enjambre en estado actual.
mejor_valor : `floar`
el mejor valor del enjambre en su estado actual.
historico_particulas : `list`
lista con el estado de las partículas en cada una de las iteraciones que
ha tenido el enjambre.
historico_mejor_posicion : `list`
lista con la mejor posición en cada una de las iteraciones que ha tenido
el enjambre.
historico_mejor_valor : `list`
lista con el mejor valor en cada una de las iteraciones que ha tenido el
enjambre.
diferencia_abs : `list`
diferencia absoluta entre el mejor valor de iteraciones consecutivas.
resultados_df : `pandas.core.frame.DataFrame`
dataframe con la información del mejor valor y posición encontrado en
cada iteración, así como la mejora respecto a la iteración anterior.
valor_optimo : `float`
mejor valor encontrado en todas las iteraciones.
posicion_optima : `numpy.narray`
posición donde se ha encontrado el valor_optimo.
optimizado : `bool`
si el enjambre ha sido optimizado.
iter_optimizacion : `int`
número de iteraciones de optimizacion.
Examples
--------
Ejemplo crear enjambre
>>> enjambre = Enjambre(
n_particulas = 5,
n_variables = 3,
limites_inf = [-5,-5,-5],
limites_sup = [5,5,5],
verbose = True
)
"""
def __init__(self, n_particulas, n_variables, limites_inf = None,
limites_sup = None, verbose = False):
# Número de partículas del enjambre
self.n_particulas = n_particulas
# Número de variables de cada partícula
self.n_variables = n_variables
# Límite inferior de cada variable
self.limites_inf = limites_inf
# Límite superior de cada variable
self.limites_sup = limites_sup
# Lista de las partículas del enjambre
self.particulas = []
# Etiqueta para saber si el enjambre ha sido optimizado
self.optimizado = False
# Número de iteraciones de optimización llevadas a cabo
self.iter_optimizacion = None
# Mejor partícula del enjambre
self.mejor_particula = None
# Mejor valor del enjambre
self.mejor_valor = None
# Posición del mejor valor del enjambre.
self.mejor_posicion = None
# Estado de todas las partículas del enjambre en cada iteración.
self.historico_particulas = []
# Mejor posición en cada iteración.
self.historico_mejor_posicion = []
# Mejor valor en cada iteración.
self.historico_mejor_valor = []
# Diferencia absoluta entre el mejor valor de iteraciones consecutivas.
self.diferencia_abs = []
# data.frame con la información del mejor valor y posición encontrado en
# cada iteración, así como la mejora respecto a la iteración anterior.
self.resultados_df = None
# Mejor valor de todas las iteraciones
self.valor_optimo = None
# Mejor posición de todas las iteraciones
self.posicion_optima = None
# CONVERSIONES DE TIPO INICIALES
# ----------------------------------------------------------------------
# Si limites_inf o limites_sup no son un array numpy, se convierten en
# ello.
if self.limites_inf is not None \
and not isinstance(self.limites_inf,np.ndarray):
self.limites_inf = np.array(self.limites_inf)
if self.limites_sup is not None \
and not isinstance(self.limites_sup,np.ndarray):
self.limites_sup = np.array(self.limites_sup)
# SE CREAN LAS PARTÍCULAS DEL ENJAMBRE Y SE ALMACENAN
# ----------------------------------------------------------------------
for i in np.arange(n_particulas):
particula_i = Particula(
n_variables = self.n_variables,
limites_inf = self.limites_inf,
limites_sup = self.limites_sup,
verbose = verbose
)
self.particulas.append(particula_i)
# INFORMACIÓN DEL PROCESO (VERBOSE)
# ----------------------------------------------------------------------
if verbose:
print("---------------")
print("Enjambre creado")
print("---------------")
print("Número de partículas: " + str(self.n_particulas))
print("Límites inferiores de cada variable: "
+ str(self.limites_inf))
print("Límites superiores de cada variable: " \
+ str(self.limites_sup))
print("")
def __repr__(self):
"""
Información que se muestra cuando se imprime un objeto enjambre.
"""
texto = "============================" \
+ "\n" \
+ " Enjambre" \
+ "\n" \
+ "============================" \
+ "\n" \
+ "Número de partículas: " + str(self.n_particulas) \
+ "\n" \
+ "Límites inferiores de cada variable: " + str(self.limites_inf) \
+ "\n" \
+ "Límites superiores de cada variable: " + str(self.limites_sup) \
+ "\n" \
+ "Optimizado: " + str(self.optimizado) \
+ "\n" \
+ "Iteraciones optimización: " + str(self.iter_optimizacion) \
+ "\n" \
+ "\n" \
+ "Información mejor partícula:" \
+ "\n" \
+ "----------------------------" \
+ "\n" \
+ "Mejor posición actual: " + str(self.mejor_posicion) \
+ "\n" \
+ "Mejor valor actual: " + str(self.mejor_valor) \
+ "\n" \
+ "\n" \
+ "Resultados tras optimizar:" \
+ "\n" \
+ "----------------------------" \
+ "\n" \
+ "Posición óptima: " + str(self.posicion_optima) \
+ "\n" \
+ "Valor óptimo: " + str(self.valor_optimo)
return(texto)
def mostrar_particulas(self, n=None):
"""
Este método muestra la información de cada una de las n primeras
partículas del enjambre.
Parameters
----------
n : `int`
número de particulas que se muestran. Si no se indica el valor
(por defecto ``None``), se muestran todas. Si el valor es mayor
que `self.n_particulas` se muestran todas.
Examples
--------
>>> enjambre = Enjambre(
n_particulas = 5,
n_variables = 3,
limites_inf = [-5,-5,-5],
limites_sup = [5,5,5],
verbose = True
)
>>> enjambre.mostrar_particulas(n = 1)
"""
if n is None:
n = self.n_particulas
elif n > self.n_particulas:
n = self.n_particulas
for i in np.arange(n):
print(self.particulas[i])
return(None)
def evaluar_enjambre(self, funcion_objetivo, optimizacion, verbose = False):
"""
Este método evalúa todas las partículas del enjambre, actualiza sus
valores e identifica la mejor partícula.
Parameters
----------
funcion_objetivo : `function`
función que se quiere optimizar.
optimizacion : {maximizar o minimizar}
Dependiendo de esto, el mejor valor histórico de la partícula será
el mayor o el menor valorque ha tenido hasta el momento.
verbose : `bool`, optional
mostrar información del proceso por pantalla. (default is ``False``)
Examples
--------
Ejemplo evaluar enjambre
>>> enjambre = Enjambre(
n_particulas = 5,
n_variables = 3,
limites_inf = [-5,-5,-5],
limites_sup = [5,5,5],
verbose = True
)
>>> def funcion_objetivo(x_0, x_1, x_2):
f= x_0**2 + x_1**2 + x_2**2
return(f)
>>> enjambre.evaluar_enjambre(
funcion_objetivo = funcion_objetivo,
optimizacion = "minimizar",
verbose = True
)
"""
# SE EVALÚA CADA PARTÍCULA DEL ENJAMBRE
# ----------------------------------------------------------------------
for i in np.arange(self.n_particulas):
self.particulas[i].evaluar_particula(
funcion_objetivo = funcion_objetivo,
optimizacion = optimizacion,
verbose = verbose
)
# MEJOR PARTÍCULA DEL ENJAMBRE
# ----------------------------------------------------------------------
# Se identifica la mejor partícula de todo el enjambre. Si se está
# maximizando, la mejor partícula es aquella con mayor valor.
# Lo contrario si se está minimizando.
# Se selecciona inicialmente como mejor partícula la primera.
self.mejor_particula = copy.deepcopy(self.particulas[0])
# Se comparan todas las partículas del enjambre.
for i in np.arange(self.n_particulas):
if optimizacion == "minimizar":
if self.particulas[i].valor < self.mejor_particula.valor:
self.mejor_particula = copy.deepcopy(self.particulas[i])
else:
if self.particulas[i].valor > self.mejor_particula.valor:
self.mejor_particula = copy.deepcopy(self.particulas[i])
# Se extrae la posición y valor de la mejor partícula y se almacenan
# como mejor valor y posición del enjambre.
self.mejor_valor = self.mejor_particula.valor
self.mejor_posicion = self.mejor_particula.posicion
# INFORMACIÓN DEL PROCESO (VERBOSE)
# ----------------------------------------------------------------------
if verbose:
print("-----------------")
print("Enjambre evaluado")
print("-----------------")
print("Mejor posición encontrada : " + str(self.mejor_posicion))
print("Mejor valor encontrado : " + str(self.mejor_valor))
print("")
def mover_enjambre(self, inercia, peso_cognitivo, peso_social,
verbose = False):
"""
Este método mueve todas las partículas del enjambre.
Parameters
----------
optimizacion : {'maximizar', 'minimizar'}
si se desea maximizar o minimizar la función.
inercia : `float` or `int`
coeficiente de inercia.
peso_cognitivo : `float` or `int`
coeficiente cognitivo.
peso_social : `float` or `int`
coeficiente social.
verbose : `bool`, optional
mostrar información del proceso por pantalla. (default is ``False``)
"""
# Se actualiza la posición de cada una de las partículas que forman el
# enjambre.
for i in np.arange(self.n_particulas):
self.particulas[i].mover_particula(
mejor_p_enjambre = self.mejor_posicion,
inercia = inercia,
peso_cognitivo = peso_cognitivo,
peso_social = peso_social,
verbose = verbose
)
# Información del proceso (VERBOSE)
# ----------------------------------------------------------------------
if verbose:
print("---------------------------------------------------------" \
"------------")
print("La posición de todas las partículas del enjambre ha sido " \
"actualizada.")
print("---------------------------------------------------------" \
"------------")
print("")
def optimizar(self, funcion_objetivo, optimizacion, n_iteraciones = 50,
inercia = 0.8, reduc_inercia = True, inercia_max = 0.9,
inercia_min = 0.4, peso_cognitivo = 2, peso_social = 2,
parada_temprana = False, rondas_parada = None,
tolerancia_parada = None, verbose = False):
"""
Este método realiza el proceso de optimización de un enjambre.
Parameters
----------
funcion_objetivo : `function`
función que se quiere optimizar.
optimizacion : {'maximizar' o 'minimizar'}
si se desea maximizar o minimizar la función.
m_iteraciones : `int` , optional
numero de iteraciones de optimización. (default is ``50``)
inercia : `float` or `int`, optional
coeficiente de inercia. (default is ``0.8``)
peso_cognitivo : `float` or `int`, optional
coeficiente cognitivo. (default is ``2``)
peso_social : `float` or `int`, optional
coeficiente social. (default is ``2``)
reduc_inercia: `bool`, optional
activar la reducción del coeficiente de inercia. En tal caso, el
argumento `inercia` es ignorado. (default is ``True``)
inercia_max : `float` or `int`, optional
valor inicial del coeficiente de inercia si se activa `reduc_inercia`.
(default is ``0.9``)
inercia_min : `float` or `int`, optional
valor minimo del coeficiente de inercia si se activa `reduc_min`.
(default is ``0.4``)
parada_temprana : `bool`, optional
si durante las últimas `rondas_parada` iteraciones la diferencia
absoluta entre mejores partículas no es superior al valor de
`tolerancia_parada`, se detiene el algoritmo y no se crean nuevas
iteraciones. (default is ``False``)
rondas_parada : `int`, optional
número de iteraciones consecutivas sin mejora mínima para que se
active la parada temprana. (default is ``None``)
tolerancia_parada : `float` or `int`, optional
valor mínimo que debe tener la diferencia de iteraciones consecutivas
para considerar que hay cambio. (default is ``None``)
verbose : `bool`, optional
mostrar información del proceso por pantalla. (default is ``False``)
Raises
------
raise Exception
si se indica `parada_temprana = True` y los argumentos `rondas_parada`
o `tolerancia_parada` son ``None``.
raise Exception
si se indica `reduc_inercia = True` y los argumentos `inercia_max`
o `inercia_min` son ``None``.
Examples
--------
Ejemplo optimización
>>> def funcion_objetivo(x_0, x_1):
# Para la región acotada entre −10<=x_0<=0 y −6.5<=x_1<=0 la
# función tiene múltiples mínimos locales y un único mínimo
# global en f(−3.1302468,−1.5821422)= −106.7645367.
f = np.sin(x_1)*np.exp(1-np.cos(x_0))**2 \
+ np.cos(x_0)*np.exp(1-np.sin(x_1))**2 \
+ (x_0-x_1)**2
return(f)
>>> enjambre = Enjambre(
n_particulas = 50,
n_variables = 2,
limites_inf = [-10, -6.5],
limites_sup = [0, 0],
verbose = False
)
>>> enjambre.optimizar(
funcion_objetivo = funcion_objetivo,
optimizacion = "minimizar",
n_iteraciones = 250,
inercia = 0.8,
reduc_inercia = True,
inercia_max = 0.9,
inercia_min = 0.4,
peso_cognitivo = 1,
peso_social = 2,
parada_temprana = True,
rondas_parada = 5,
tolerancia_parada = 10**-3,
verbose = False
)
"""
# COMPROBACIONES INICIALES: EXCEPTIONS Y WARNINGS
# ----------------------------------------------------------------------
# Si se activa la parada temprana, hay que especificar los argumentos
# rondas_parada y tolerancia_parada.
if parada_temprana \
and (rondas_parada is None or tolerancia_parada is None):
raise Exception(
"Para activar la parada temprana es necesario indicar un " \
+ " valor de rondas_parada y de tolerancia_parada."
)
# Si se activa la reducción de inercia, hay que especificar los argumentos
# inercia_max y inercia_min.
if reduc_inercia \
and (inercia_max is None or inercia_min is None):
raise Exception(
"Para activar la reducción de inercia es necesario indicar un " \
+ "valor de inercia_max y de inercia_min."
)
# ITERACIONES
# ----------------------------------------------------------------------
start = time.time()
for i in np.arange(n_iteraciones):
if verbose:
print("-------------")
print("Iteracion: " + str(i))
print("-------------")
# EVALUAR PARTÍCULAS DEL ENJAMBRE
# ------------------------------------------------------------------
self.evaluar_enjambre(
funcion_objetivo = funcion_objetivo,
optimizacion = optimizacion,
verbose = verbose
)
# SE ALMACENA LA INFORMACIÓN DE LA ITERACIÓN EN LOS HISTÓRICOS
# ------------------------------------------------------------------
self.historico_particulas.append(copy.deepcopy(self.particulas))
self.historico_mejor_posicion.append(copy.deepcopy(self.mejor_posicion))
self.historico_mejor_valor.append(copy.deepcopy(self.mejor_valor))
# SE CALCULA LA DIFERENCIA ABSOLUTA RESPECTO A LA ITERACIÓN ANTERIOR
# ------------------------------------------------------------------
# La diferencia solo puede calcularse a partir de la segunda
# iteración.
if i == 0:
self.diferencia_abs.append(None)
else:
diferencia = abs(self.historico_mejor_valor[i] \
- self.historico_mejor_valor[i-1])
self.diferencia_abs.append(diferencia)
# CRITERIO DE PARADA
# ------------------------------------------------------------------
# Si durante las últimas n iteraciones, la diferencia absoluta entre
# mejores partículas no es superior al valor de tolerancia_parada,
# se detiene el algoritmo y no se crean nuevas iteraciones.
if parada_temprana and i > rondas_parada:
ultimos_n = np.array(self.diferencia_abs[-(rondas_parada): ])
if all(ultimos_n < tolerancia_parada):
print("Algoritmo detenido en la iteracion "
+ str(i) \
+ " por falta cambio absoluto mínimo de " \
+ str(tolerancia_parada) \
+ " durante " \
+ str(rondas_parada) \
+ " iteraciones consecutivas.")
break
# MOVER PARTÍCULAS DEL ENJAMBRE
# ------------------------------------------------------------------
# Si se ha activado la reducción de inercia, se recalcula su valor
# para la iteración actual.
if reduc_inercia:
inercia = ((inercia_max - inercia_min) \
* (n_iteraciones-i)/n_iteraciones) \
+ inercia_min
self.mover_enjambre(
inercia = inercia,
peso_cognitivo = peso_cognitivo,
peso_social = peso_social,
verbose = False
)
end = time.time()