Código Python¶
Individuo¶
-
class
ga.class_individuo.
Individuo
(n_variables, limites_inf=None, limites_sup=None, verbose=False)[source]¶ Esta clase representa un individuo con unas características inicial definida por una combinación de valores numéricos aleatorios. El rango de posibles valores para cada variable puede estar acotado.
Parameters: - n_variables (int) – número de variables que definen al individuo.
- 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
. LosNone
serán remplazados por el valor (-10**3). (defaultNone
) - 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
. LosNone
serán remplazados por el valor (+10**3). (defaultNone
) - verbose (bool, optional) – mostrar información del individuo creado. (default
False
)
-
n_variables
¶ número de variables que definen al individuo.
Type: int
-
limites_inf
¶ límite inferior de cada variable. Si solo se quiere predefinir límites de alguna variable, emplear
None
. LosNone
serán remplazados por el valor (-10**3).Type: list or numpy.ndarray
-
limites_sup
¶ límite superior de cada variable. Si solo se quiere predefinir límites de alguna variable, emplear
None
. LosNone
serán remplazados por el valor (+10**3).Type: list or numpy.ndarray
-
valor_variables
¶ array con el valor de cada una de las variables.
Type: numpy.ndarray
-
fitness
¶ valor de fitness del individuo.
Type: float
-
valor_funcion
¶ valor de la función objetivo para el individuo.
Type: float
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 individuo.
>>> individuo = Individuo( n_variables = 3, limites_inf = [-1,2,0], limites_sup = [4,10,20], verbose = True )
Methods
calcular_fitness
(funcion_objetivo, optimizacion)Este método obtiene el fitness del individuo calculando el valor que toma la función objetivo con el valor de sus variables. mutar
([prob_mut, distribucion, …])Este método somete al individuo a un proceso de mutación en el que, cada una de sus posiciones, puede verse modificada con una probabilidad prob_mut. -
calcular_fitness
(funcion_objetivo, optimizacion, verbose=False)[source]¶ Este método obtiene el fitness del individuo calculando el valor que toma la función objetivo con el valor de sus variables.
Parameters: - funcion_objetivo (function) – función que se quiere optimizar.
- optimizacion ({'maximizar', 'minimizar'}) – ver notas para más información.
- verbose (bool, optional) – mostrar información del proceso por pantalla. (default
False
)
Raises: raise Exception – si el argumento optimizacion es distinto de ‘maximizar’ o ‘minimizar’
Notes
Cada individuo de la población debe ser evaluado para cuantificar su bondad como solución al problema, a esta cuantificación se le llama fitness.
Dependiendo de si se trata de un problema de maximización o minimización, la relación del fitness con la función objetivo \(f\) puede ser:
Maximización: el individuo tiene mayor fitness cuanto mayor es el valor de la función objetivo \(f(individuo)\).
Minimización: el individuo tiene mayor fitness cuanto menor es el valor de la función objetivo \(f(individuo)\), o lo que es lo mismo, cuanto mayor es el valor de la función objetivo, menor el fitness. El algoritmo genético selecciona los individuos de mayor fitness, por lo que, para problemas de minimización, el fitness puede calcularse como \(−f(individuo)\) o también \(\frac{1}{1+f(individuo)}\).
Examples
Ejemplo evaluar individuo con una función objetivo.
>>> individuo = Individuo( 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)
>>> individuo.calcular_fitness( funcion_objetivo = funcion_objetivo, optimizacion = "minimizar", verbose = True )
-
mutar
(prob_mut=0.01, distribucion='uniforme', media_distribucion=1, sd_distribucion=1, min_distribucion=-1, max_distribucion=1, verbose=False)[source]¶ Este método somete al individuo a un proceso de mutación en el que, cada una de sus posiciones, puede verse modificada con una probabilidad prob_mut. Tras mutar, los atributos valor_funcion y fitness se reinician.
Parameters: - prob_mut (float, optional) – probabilidad que tiene cada posición del individuo de mutar. (default 0.01)
- distribucion ({"normal", "uniforme", "aleatoria"}, optional) – distribución de la que obtener el factor de mutación. (default “uniforme”)
- media_distribucion (float, optional) – media de la distribución si se selecciona distribucion = “normal” (default 1)
- sd_distribucion (float, optional) – desviación estándar de la distribución si se selecciona distribucion = “normal”. (default 1)
- min_distribucion (float, optional) – mínimo de la distribución si se selecciona distribucion = “uniforme”. (default -1)
- max_distribucion (float, optional) – máximo de la distribución si se selecciona distribucion = “uniforme”. (default +1)
- verbose (bool, optional) – mostrar información del proceso por pantalla. (default
False
)
Raises: raise Exception – si el argumento distribucion es distinto de ‘normal’, ‘uniforme’ o ‘aleatoria’.
Examples
Ejemplo mutar individuo.
>>> individuo = Individuo( n_variables = 3, limites_inf = [-1,2,0], limites_sup = [4,10,20], verbose = True )
>>> individuo.mutar( prob_mut = 0.5, distribucion = "uniforme", min_distribucion = -1, max_distribucion = 1, verbose = True )
Notes
El proceso de mutación añade diversidad al proceso y evitar que el algoritmo caiga en mínimos locales por que todos los individuos sean demasiado parecidos de una generación a otra. Existen diferentes estrategias para controlar la magnitud del cambio que puede provocar una mutación.
Distribución uniforme: la mutación de la posición i se consigue sumándole al valor de i un valor extraído de una distribución uniforme, por ejemplo una entre [-1,+1].
Distribución normal: la mutación de la posición i se consigue sumándole al valor de i un valor extraído de una distribución normal, comúnmente centrada en 0 y con una determinada desviación estándar. Cuanto mayor la desviación estándar, con mayor probabilidad la mutación introducirá cambios grandes.
Aleatorio: la mutación de la posición i se consigue reemplazando el valor de i por nuevo valor aleatorio dentro del rango permitido para esa variable. Esta estrategia suele conllevar mayores variaciones que las dos anteriores.
Hay que tener en cuenta que, debido a las mutaciones, un valor que inicialmente estaba dentro del rango permitido puede salirse de él. Una forma de evitarlo es: si el valor tras la mutación excede alguno de los límites acotados, se sobrescribe con el valor del límite. Es decir, se permite que los valores se alejen como máximo hasta el límite impuesto.
Población¶
-
class
ga.class_poblacion.
Poblacion
(n_individuos, n_variables, limites_inf=None, limites_sup=None, verbose=False)[source]¶ Esta clase crea una población de n individuos.
Parameters: - n_individuos (int) – número de individuos de la población.
- n_variables (int) – número de variables que definen a cada individuo.
- limites_inf (list or numpy.ndarray) – límite inferior de cada variable. Si solo se quiere predefinir límites
de alguna variable, emplear
None
. LosNone
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
False
)
-
individuos
¶ lista con todos los individuos de la población en su estado actual.
Type: list
-
n_individuos
¶ número de individuos de la población.
Type: int
-
n_variables
¶ número de variables que definen a cada individuo.
Type: int
-
limites_inf
¶ límite inferior de cada variable.
Type: list or numpy.ndarray
-
limites_sup
¶ límite superior de cada variable.
Type: list or numpy.ndarray
-
mejor_individuo
¶ mejor individuo de la población en su estado actual.
Type: object individuo
-
mejor_fitness
¶ fitness del mejor individuo de la población en su estado actual.
Type: float
-
mejor_valor_funcion
¶ valor de la función objetivo del mejor individuo de la población en su estado actual.
Type: float
-
mejor_individuo_variables
¶ valor de las variables del mejor individuo de la población en su estado actual.
Type: numpy.ndarray
-
historico_individuos
¶ lista con la información de todos los individuos en cada una de las generaciones que ha tenido la población.
Type: list
-
historico_mejor_individuo_variables
¶ lista con valor de las variables del mejor individuo en cada una de las generaciones que ha tenido la población.
Type: list
-
historico_mejor_fitness
¶ lista con el mejor fitness en cada una de las generaciones que ha tenido la población.
Type: list
-
historico_mejor_valor_funcion
¶ lista con valor de la función objetivo del mejor individuo en cada una de las generaciones que ha tenido la población.
Type: list
-
diferencia_abs
¶ diferencia absoluta entre el mejor fitness de generaciones consecutivas.
Type: list
-
resultados_df
¶ dataframe con la información del mejor fitness y valor de las variables encontrado en cada generación, así como la diferencia respecto a la generación anterior.
Type: pandas.core.frame.DataFrame
-
fitness_optimo
¶ mejor fitness encontrado tras el proceso de optimización.
Type: float
-
valor_funcion_optimo
¶ valor de la función objetivo encontrado tras el proceso de optimización.
Type: float
-
valor_variables_optimo
¶ valor de las variables con el que se ha conseguido el mejor fitness tras el proceso de optimización.
Type: numpy.narray
-
optimizado
¶ si la población ha sido optimizada.
Type: bool
-
iter_optimizacion
¶ número de iteraciones de optimización (generaciones).
Type: int
Examples
Ejemplo crear población
>>> poblacion = Poblacion( n_individuos = 3, n_variables = 3, limites_inf = [-5,-5,-5], limites_sup = [5,5,5], verbose = True )
Methods
crear_nueva_generecion
([metodo_seleccion, …])Este método somete la población a una nueva generación. cruzar_individuos
(parental_1, parental_2[, …])Este método genera un nuevo individuo a partir de dos individuos parentales empleando el método de cruzamiento uniforme. evaluar_poblacion
(funcion_objetivo, optimizacion)Este método calcula el fitness de todos los individuos de la población, actualiza sus valores e identifica el mejor. mostrar_individuos
([n])Este método muestra la información de cada uno de los n primeros individuos de la población. optimizar
(funcion_objetivo, optimizacion[, …])Este método realiza el proceso de optimización de una población. seleccionar_individuo
(n[, return_indices, …])Este método selecciona los índices de n individuos de una población, donde la probabilidad de selección está relacionada con el fitness de cada individuo. -
crear_nueva_generecion
(metodo_seleccion='tournament', elitismo=0.1, prob_mut=0.01, distribucion='uniforme', media_distribucion=1, sd_distribucion=1, min_distribucion=-1, max_distribucion=1, verbose=False, verbose_seleccion=False, verbose_cruce=False, verbose_mutacion=False)[source]¶ Este método somete la población a una nueva generación.
Parameters: - metodo_seleccion ({"ruleta", "rank", "tournament"}) – método de selección de selección, ver notas para más información. (default tournament)
- elitismo (float, optional) – porcentaje de mejores individuos de la población actual que pasan directamente a la siguiente población. De esta forma, se asegura que, la siguiente generación, no sea nunca peor. (default 0.1)
- prob_mut (float, optional) – probabilidad que tiene cada posición del individuo de mutar. (default 0.01)
- distribucion ({"normal", "uniforme", "aleatoria"}, optional) – distribución de la que obtener el factor de mutación. (default “uniforme”)
- media_distribucion (float, optional) – media de la distribución si se selecciona distribucion = “normal” (default 1)
- sd_distribucion (float, optional) – desviación estándar de la distribución si se selecciona distribucion = “normal”. (default 1)
- min_distribucion (float, optional) – mínimo de la distribución si se selecciona distribucion = “uniforme”. (default -1)
- max_distribucion (float, optional) – máximo de la distribución si se selecciona distribucion = “uniforme”. (default +1)
- verbose (bool, optional) – mostrar información del proceso por pantalla. (default
False
) - verbose_seleccion (bool, optional) – mostrar información de cada selección por pantalla.
(default
False
) - verbose_cruce (bool, optional) – mostrar información de cada cruce por pantalla.
(default
False
) - verbose_mutacion (bool, optional) – mostrar información de cada mutación por pantalla.
(default
False
)
Examples
>>> poblacion = Poblacion( n_individuos = 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)
>>> poblacion.evaluar_poblacion( funcion_objetivo = funcion_objetivo, optimizacion = "minimizar", verbose = True )
>>> poblacion.crear_nueva_generecion( metodo_seleccion = "tournament", elitismo = 0.1, prob_mut = 0.01, distribucion = "uniforme", verbose = True, verbose_seleccion = False, verbose_cruce = False, verbose_mutacion = False )
-
cruzar_individuos
(parental_1, parental_2, verbose=False)[source]¶ Este método genera un nuevo individuo a partir de dos individuos parentales empleando el método de cruzamiento uniforme.
Parameters: - parental_1 (int) – índice del individuo de la población que se quiere emplear como parental 1 para el cruzamiento.
- parental_1 – índice del individuo de la población que se quiere emplear como parental 1 para el cruzamiento.
- verbose (bool, optional) – mostrar información del proceso por pantalla. (default
False
)
Raises: raise Exception – si los índices parental_1 o parental_2 no son índices válidos.
Returns: descendencia – Nuevo individuo generado por cruzamiento de dos parentales.
Return type: Individuo
Examples
>>> poblacion = Poblacion( n_individuos = 5, n_variables = 3, limites_inf = [-5,-5,-5], limites_sup = [5,5,5] )
>>> descendencia = poblacion.cruzar_individuos( parental_1 = 0, parental_2 = 1, verbose = True )
Notes
El objetivo del cruzamiento es generar, a partir de individuos ya existentes (parentales), nuevos individuos (descendencia) que combinen las características de los anteriores. Este es otro de los puntos del algoritmo en los que se puede seguir varias estrategias. Tres de las más empleadas son:
Cruzamiento a partir de uno solo punto: se selecciona aleatoriamente una posición que actúa como punto de corte. Cada individuo parental se divide en dos partes y se intercambian las mitades. Como resultado de este proceso, por cada cruce, se generan dos nuevos individuos.
Cruzamiento a partir múltiples puntos: se seleccionan aleatoriamente varias posiciones que actúan como puntos de corte. Cada individuo parental se divide por los puntos de corte y se intercambian las partes. Como resultado de este proceso, por cada cruce, se generan dos nuevos individuos.
Cruzamiento uniforme: el valor que toma cada posición del nuevo individuo se obtiene de uno de los dos parentales. Por lo general, la probabilidad de que el valor proceda de cada parental es la misma, aunque podría, por ejemplo, estar condicionada al fitness de cada uno. A diferencia de las anteriores estrategias, con esta, de cada cruce se genera un único descendiente.
-
evaluar_poblacion
(funcion_objetivo, optimizacion, verbose=False)[source]¶ Este método calcula el fitness de todos los individuos de la población, actualiza sus valores e identifica el mejor.
Parameters: - funcion_objetivo (function) – función que se quiere optimizar.
- optimizacion ({"maximizar" o "minimizar"}) – si se desea maximizar o minimizar la función.
- verbose (bool, optional) – mostrar información del proceso por pantalla. (default
False
)
Examples
Ejemplo evaluar población
>>> poblacion = Poblacion( n_individuos = 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)
>>> poblacion.evaluar_poblacion( funcion_objetivo = funcion_objetivo, optimizacion = "minimizar", verbose = True )
-
mostrar_individuos
(n=None)[source]¶ Este método muestra la información de cada uno de los n primeros individuos de la población.
Parameters: n (int) – número de individuos que se muestran. Si no se indica el valor (por defecto None
), se muestran todas. Si el valor es mayor que self.n_individuos se muestran todas.Examples
>>> poblacion = Poblacion( n_individuos = 5, n_variables = 3, limites_inf = [-5,-5,-5], limites_sup = [5,5,5], verbose = True )
>>> poblacion.mostrar_individuos(n = 1)
-
optimizar
(funcion_objetivo, optimizacion, n_generaciones=50, metodo_seleccion='tournament', elitismo=0.1, prob_mut=0.01, distribucion='uniforme', media_distribucion=1, sd_distribucion=1, min_distribucion=-1, max_distribucion=1, parada_temprana=False, rondas_parada=None, tolerancia_parada=None, verbose=False, verbose_nueva_generacion=False, verbose_seleccion=False, verbose_cruce=False, verbose_mutacion=False, verbose_evaluacion=False)[source]¶ Este método realiza el proceso de optimización de una población.
Parameters: - funcion_objetivo (function) – función que se quiere optimizar.
- optimizacion ({"maximizar" o "minimizar"}) – si se desea maximizar o minimizar la función.
- n_generaciones (int , optional) – número de generaciones de optimización. (default
50
) - metodo_seleccion ({"ruleta", "rank", "tournament"}) – método de selección de selección, ver notas para más información. (default tournament)
- elitismo (float, optional) – porcentaje de mejores individuos de la población actual que pasan directamente a la siguiente población. De esta forma, se asegura que, la siguiente generación, no sea nunca peor. (default 0.1)
- prob_mut (float, optional) – probabilidad que tiene cada posición del individuo de mutar. (default 0.01)
- distribucion ({"normal", "uniforme", "aleatoria"}, optional) – distribución de la que obtener el factor de mutación. (default “uniforme”)
- media_distribucion (float, optional) – media de la distribución si se selecciona distribucion = “normal” (default 1)
- sd_distribucion (float, optional) – desviación estándar de la distribución si se selecciona distribucion = “normal”. (default 1)
- min_distribucion (float, optional) – mínimo de la distribución si se selecciona distribucion = “uniforme”. (default -1)
- max_distribucion (float, optional) – máximo de la distribución si se selecciona distribucion = “uniforme”. (default +1)
- parada_temprana (bool, optional) – si durante las últimas rondas_parada generaciones la diferencia
absoluta entre mejores individuos no es superior al valor de
tolerancia_parada, se detiene el algoritmo y no se crean nuevas
generaciones. (default
False
) - rondas_parada (int, optional) – número de generaciones consecutivas sin mejora mínima para que se
active la parada temprana. (default
None
) - tolerancia_parada (float or int, optional) – valor mínimo que debe tener la diferencia de generaciones consecutivas
para considerar que hay cambio. (default
None
) - verbose (bool, optional) – mostrar información del proceso por pantalla. (default
False
) - verbose_nueva_generacion (bool, optional) – mostrar información de cada nueva generación por pantalla.
(default
False
) - verbose_seleccion (bool, optional) – mostrar información de cada selección por pantalla.
(default
False
) - verbose_cruce (bool, optional) – mostrar información de cada cruce por pantalla.
(default
False
) - verbose_mutacion (bool, optional) – mostrar información de cada mutación por pantalla.
(default
False
)
Raises: - raise Exception – si se indica parada_temprana = True y los argumentos rondas_parada
o tolerancia_parada son
None
. - raise Exception – si el argumento metodo_seleccion no es ‘ruleta’, ‘rank’ o ‘tournament’.
- raise Exception – si el argumento optimizacion es distinto de ‘maximizar’ o ‘minimizar
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 minimo # 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)
>>> poblacion = Poblacion( n_individuos = 50, n_variables = 2, limites_inf = [-10, -6.5], limites_sup = [0, 0], verbose = False )
>>> poblacion.optimizar( funcion_objetivo = funcion_objetivo, optimizacion = "minimizar", n_generaciones = 250, metodo_seleccion = "tournament", elitismo = 0.1, prob_mut = 0.01, distribucion = "uniforme", media_distribucion = 1, sd_distribucion = 1, min_distribucion = -1, max_distribucion = 1, parada_temprana = True, rondas_parada = 10, tolerancia_parada = 10**-16, verbose = False )
-
seleccionar_individuo
(n, return_indices=True, metodo_seleccion='tournament', verbose=False)[source]¶ Este método selecciona los índices de n individuos de una población, donde la probabilidad de selección está relacionada con el fitness de cada individuo. Si el argumento return_indices=False en lugar de los índices se devuelve una copia de los individuos seleccionados.
Parameters: - n (int) – número de individuos de la población seleccionados.
- return_indices (bool, optional) – cuando es True, se devuelve el indice que ocupan los individuos
seleccionados, cuando es False se devuelve una lista que contiene una
copia de los individuos. (default
True
) - metodo_seleccion ({"ruleta", "rank", "tournament"}) – método de selección de selección, ver notas para más información. (default tournament)
- verbose (bool, optional) – mostrar información del proceso por pantalla. (default
False
)
Raises: raise Exception – si el argumento metodo_seleccion no es ‘ruleta’, ‘rank’ o ‘tournament’.
Returns: - indices (numpy.ndarray) – índice de los individuos seleccionados (si return_indices=True).
- individuos (list) – lista con los individuos seleccionados (si return_indices=False).
Examples
>>> poblacion = Poblacion( n_individuos = 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)
>>> poblacion.evaluar_poblacion( funcion_objetivo = funcion_objetivo, optimizacion = "minimizar", verbose = True )
>>> poblacion.seleccionar_individuo( n = 2, return_indices = True, metodo_seleccion = "tournament", verbose = True )
Notes
La forma en que se seleccionan los individuos que participan en cada cruce difiere en las distintas implementaciones de los algoritmos genéticos. Por lo general, todas ellas tienden a favorecer la selección de aquellos individuos con mayor fitness. Algunas de las estrategias más comunes son:
Método de ruleta: la probabilidad de que un individuo sea seleccionado es proporcional a su fitness relativo, es decir, a su fitness dividido por la suma del fitness de todos los individuos de la población. Si el fitness de un individuo es el doble que el de otro, también lo será la probabilidad de que sea seleccionado. Este método presenta problemas si el fitness de unos pocos individuos es muy superior (varios órdenes de magnitud) al resto, ya que estos serán seleccionados de forma repetida y casi todos los individuos de la siguiente generación serán “hijos” de los mismos “padres” (poca variación).
Método rank: la probabilidad de selección de un individuo es inversamente proporcional a la posición que ocupa tras ordenar todos los individuos de mayor a menor fitness. Este método es menos agresivo que el método ruleta cuando la diferencia entre los mayores fitness es varios órdenes de magnitud superior al resto.
Selección competitiva (tournament): se seleccionan aleatoriamente dos parejas de individuos de la población (todos con la misma probabilidad). De cada pareja se selecciona el que tenga mayor fitness. Finalmente, se comparan los dos finalistas y se selecciona el de mayor fitness. Este método tiende a generar una distribución de la probabilidad de selección más equilibrada que las dos anteriores.
Selección truncada (truncated selection): se realizan selecciones aleatorias de individuos, habiendo descartado primero los n individuos con menor fitness de la población.