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. Los None serán remplazados por el valor (-10**3). (default 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 None)
  • 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. Los None 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. Los None 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. 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 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.