English Русский 中文 Deutsch 日本語 Português
preview
Optimización de la quimiotaxis bacteriana - Bacterial Chemotaxis Optimisation (BCO)

Optimización de la quimiotaxis bacteriana - Bacterial Chemotaxis Optimisation (BCO)

MetaTrader 5Ejemplos |
169 0
Andrey Dik
Andrey Dik

Contenido

  1. Introducción
  2. Implementación del algoritmo
  3. Resultados de las pruebas


Introducción

En el campo de la optimización, muchos investigadores y desarrolladores se inspiran en procesos biológicos que suceden en la naturaleza, como la evolución, las interacciones sociales o el comportamiento de la búsqueda de alimento de los organismos vivos. Y esto conduce al desarrollo de técnicas de optimización completamente nuevas e innovadoras. Los estudios han demostrado que estos métodos suelen superar a los enfoques heurísticos clásicos y a los basados en gradientes, sobre todo cuando se trata de resolver problemas multimodales, no diferenciables y discretos. Uno de estos métodos es el algoritmo de quimiotaxis propuesto por primera vez por Bremermannn y sus colegas; nosotros ya analizamos el algoritmo de optimización por forrajeo bacteriano (BFO) en su momento. Este modela la respuesta de las bacterias a los quimioatrayentes en gradientes de concentración. Bremermann analizó la quimiotaxis en gradientes tridimensionales y la aplicó al entrenamiento de redes neuronales. Aunque este algoritmo se basa en los principios de la quimiotaxis bacteriana, ciertos nuevos descubrimientos biológicos han proporcionado un modelo más detallado de este proceso.

En este artículo, intentaremos analizar el modelo mencionado como una nueva estrategia de optimización. Las principales diferencias entre el nuevo modelo y el algoritmo de quimiotaxis clásico son las siguientes:

  1. Las bacterias del nuevo modelo usan información sobre los valores de concentración.
  2. No continúan en la misma dirección cuando aumentan las concentraciones de quimioatrayentes.

Las bacterias son organismos unicelulares, es decir, una de las formas de vida más simples de la Tierra. A pesar de su simplicidad, son capaces de captar información sobre su entorno, orientarse con ella y utilizarla eficazmente para sobrevivir. La respuesta de las bacterias a los cambios del entorno ha sido objeto de intensas investigaciones en las últimas décadas, y también ha atraído la atención de los científicos que trabajan en el campo de la optimización. Los algoritmos de optimización pueden considerarse sistemas que recopilan información sobre el panorama funcional y la utilizan para alcanzar un grado óptimo. La simplicidad de la idea de la quimiotaxis bacteriana la convierte en un atractivo punto de partida para construir dicho tipo de algoritmos.

Diversos estudios han descubierto que las bacterias intercambian información entre sí, aunque no se sabe mucho sobre los mecanismos de dicha comunicación. Por lo general, las bacterias se contemplan como individuos y la interacción social no se tiene en cuenta en los modelos. Esto los distingue de los modelos de interacción que describen el comportamiento de los insectos sociales (como hormigas, abejas, avispas o termitas), que actúan como sistemas con inteligencia colectiva, lo cual abre distintas posibilidades para resolver diferentes problemas.

La adaptación es otro aspecto importante de la quimiotaxis. Las bacterias son capaces de cambiar su sensibilidad a condiciones químicas constantes, lo cual les permite responder eficazmente a los cambios del entorno. Esta cualidad no solo las hace resistentes, sino también muy eficaces en la búsqueda de recursos.

En este estudio, los autores se centraron en modelos microscópicos que tienen en cuenta la quimiotaxis de bacterias individuales, en contraposición a los modelos macroscópicos que analizan el movimiento de las colonias. El algoritmo fue desarrollado por S. D. Müller y P. Koumatsakas, cuyas ideas principales se presentaron y publicaron en 2002.


Implementación del algoritmo

La idea que subyace en el algoritmo de quimiotaxis bacteriana (BCO) consiste en usar los principios biológicos observados en el comportamiento bacteriano para resolver problemas de optimización. El algoritmo modela cómo responden las bacterias a los gradientes de sustancias químicas en el entorno, lo cual les permite encontrar condiciones más favorables para vivir. Ideas básicas del algoritmo:

  1. El algoritmo describe el movimiento bacteriano como una secuencia de trayectorias rectilíneas conectadas por giros instantáneos. Cada movimiento se caracteriza por su velocidad, dirección y duración.
  2. El sentido de rotación de la bacteria viene determinado por una distribución de probabilidades, lo cual permite variaciones aleatorias del movimiento.
  3. El algoritmo usa la información sobre los gradientes de la función para guiar a la bacteria hacia la solución óptima, minimizando el número de iteraciones necesarias para alcanzar el objetivo.

El pseudocódigo detallado del algoritmo BCO describe los pasos principales del algoritmo, incluida la inicialización, el ciclo principal de optimización con los pasos de movimiento y revisión, y las funciones auxiliares.

Inicialización.

1. Establecer los parámetros:

  • popSize: tamaño de la población (número de bacterias)
  • hs: número de pasos para calcular el cambio medio
  • T0: temperatura inicial
  • b: parámetro
  • tau_c: parámetro
  • v: velocidad

2. Crear un array de bacterias del tamaño popSize

3. Para cada bacteria, i abarca de 0 hasta popSize-1:

  • Inicializar fPrev con el valor -DBL_MAX
  • Crear un array fHistory de tamaño hs y rellenarlo de ceros

Ciclo básico de optimización. Repetir hasta que se alcance la condición de parada:

La etapa de movimiento para cada bacteria i de 0 a popSize será 1:

1. Obtener el valor actual de la función objetio f_tr

2. Obtener el valor anterior de la función objetivo f_pr de bacterium [i].fPrev

3. Si f_tr <= f_pr: T = T0

    En caso contrario: calcular b_corr = CalculateB corregido (f_tr, f_pr, i)

  • T = T0 * exp (b_corr * (f_tr - f_pr))

4. Generar tau a partir de una distribución exponencial con parámetro T.

5. Calcular los ángulos new_angles [] para las dimensiones coords - 1: para cada ángulo j de 0 a coords - 2:

  • theta = CalculateRotationAngle ()
  • mu = 62 * (1 - cos (theta)) * π / 180
  • sigma = 26 * (1 - cos (theta)) * π / 180
  • new_angles [j] = número aleatorio de la distribución gaussiana con parámetros (mu, mu-π, mu+π)

6. Calcular la nueva posición:

  • l = v * tau
  • CalculateNewPosition (l, new_angles, new_position, current_position)

7. Actualizar la posición de la bacteria restringiendo los valores dentro de rangeMin y rangeMax

8. Actualizar bacterium [i].fPrev con el valor f_tr

Fase de revisión.

1. Actualizar la mejor solución global cB con el valor de aptitud fB

2. Para cada bacteria i de 0 a popSize - 1. Actualizar la historia de valores de la función objetivo fHistory:

  • Desplazar todos los valores una posición a la izquierda
  • Añadir el valor actual de la función objetivo al final de la historia

Funciones auxiliares:

CalculateCorrectedB (f_tr, f_pr, bacteriumIndex)

1. Calcular delta_f_tr = f_tr - f_pr

2. Calcular delta_f_pr = cambio medio en los últimos pasos hs.

3. Si |delta_f_pr| < epsilon: Retornar b

   De lo contrario: Retornar b * (1 / (|delta_f_tr / delta_f_pr| + 1) + 1 / (|f_pr| + 1))

CalculateRotationAngle ()

1. Calcular cos_theta = exp (-tau_c / T0)

2. Retornar arccos (cos_theta)

CalculateNewPosition (l, angles, new_position, current_position)

1. Calcular new_position [0] considerando todos los ángulos

2. Para cada coordenada i de 1 a coords - 1:

   Calcular new_position [i] con respecto a los ángulos correspondientes

3. Aplicar una dirección aleatoria (1 o -1) a cada coordenada

GenerateFromExponentialDistribution (T)

Retornar -T * ln (número aleatorio de 0 a 1)

Vamos a escribir el código del algoritmo. Para representar la bacteria como solución al problema de optimización, describiremos la estructura "S_BCO_Bacterium".

1. Campos de la estructura:

  • fPrev - valor anterior de la función objetivo.
  • fHistory [] - array con la historia de los valores de la función objetivo.

2. Init - el método de inicialización realiza las siguientes acciones:

  • cambia el tamaño del array "fHistory" al valor "historySize".
  • todos los elementos del array "fHistory" se inicializan con el valor "0.0".
  • fPrev - establece el valor anterior de la función objetivo en el valor mínimo posible.

Se trata de monitorear los cambios en los valores de la función objetivo a lo largo de un número determinado de iteraciones (movimientos individuales).

//——————————————————————————————————————————————————————————————————————————————
struct S_BCO_Bacterium
{
    double fPrev;        // previous value of the objective function
    double fHistory [];  // history of objective function values

    void Init (int coords, int historySize)
    {
      ArrayResize     (fHistory, historySize);
      ArrayInitialize (fHistory, 0.0);
      fPrev = -DBL_MAX;
    }
};
//——————————————————————————————————————————————————————————————————————————————

Luego describiremos la clase del algoritmo "C_AO_BCO". Vamos a desglosarla pieza por pieza.

1. El constructor inicializará los parámetros externos del algoritmo.

2. El método "SetParams" actualizará los valores de los parámetros del array "params".

3. Los métodos "Moving" y "Revision" se encargarán de desplazar las bacterias y revisar sus posiciones.

4. La clase definirá varios métodos privados que se utilizarán para diversos cálculos relacionados con el algoritmo: "CalculateAverageDeltaFpr", "CalculateNewAngles", "CalculateNewPosition", "GenerateFromExponentialDistribution", "CalculateCorrectedB", "CalculateRotationAngle", "RNDdir", bacterium - array de bacterias (población). Parámetros de la clase:

  • hs - número de pasos para calcular el cambio medio.
  • T0 - temperatura inicial.
  • b - parámetro.
  • tau_c - parámetro.
  • v - velocidad.
//——————————————————————————————————————————————————————————————————————————————
class C_AO_BCO : public C_AO
{
  public: //--------------------------------------------------------------------
  ~C_AO_BCO () { }
  C_AO_BCO ()
  {
    ao_name = "BCO";
    ao_desc = "Bacterial Chemotaxis Optimization";
    ao_link = "https://www.mql5.com/es/articles/15711";

    popSize = 50;     // population size (number of bacteria)
    hs      = 10;     // number of steps to calculate average change
    T0      = 1.0;    // initial temperature
    b       = 0.5;    // parameter b
    tau_c   = 1.0;    // parameter tau_c
    v       = 1.0;    // velocity

    ArrayResize (params, 6);

    params [0].name = "popSize"; params [0].val = popSize;
    params [1].name = "hs";      params [1].val = hs;
    params [2].name = "T0";      params [2].val = T0;
    params [3].name = "b";       params [3].val = b;
    params [4].name = "tau_c";   params [4].val = tau_c;
    params [5].name = "v";       params [5].val = v;
  }

  void SetParams ()
  {
    popSize = (int)params [0].val;
    hs      = (int)params [1].val;
    T0      = params      [2].val;
    b       = params      [3].val;
    tau_c   = params      [4].val;
    v       = params      [5].val;
  }

  bool Init (const double &rangeMinP  [], //minimum search range
             const double &rangeMaxP  [], //maximum search range
             const double &rangeStepP [], //step search
             const int     epochsP = 0);  //number of epochs

  void Moving ();
  void Revision ();

  //----------------------------------------------------------------------------
  int    hs;
  double T0;
  double b;
  double tau_c;
  double v;

  S_BCO_Bacterium bacterium [];

  private: //-------------------------------------------------------------------
  double CalculateAverageDeltaFpr            (int bacteriumIndex);
  void   CalculateNewAngles                  (double &angles []);
  void   CalculateNewPosition                (double l, const double &angles [], double &new_position [], const double &current_position []);
  double GenerateFromExponentialDistribution (double T);
  double CalculateCorrectedB                 (double f_tr, double f_pr, int bacteriumIndex);
  double CalculateRotationAngle              ();
  int    RNDdir                              ();
};

//——————————————————————————————————————————————————————————————————————————————
bool C_AO_BCO::Init (const double &rangeMinP  [], //minimum search range
                     const double &rangeMaxP  [], //maximum search range
                     const double &rangeStepP [], //step search
                     const int     epochsP = 0)   //number of epochs
{
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //----------------------------------------------------------------------------
  ArrayResize (bacterium, popSize);
  for (int i = 0; i < popSize; i++) bacterium [i].Init (coords, hs);

  return true;
}
//——————————————————————————————————————————————————————————————————————————————

El método "Moving" de la clase "C_AO_BCO" se encargará de desplazar la bacteria en el espacio de búsqueda. Veamos cómo funciona:

1. Si "revision" es igual a "false", significa que las bacterias tendrán una posición inicial.

  • Para cada bacteria "a[i]", se generarán coordenadas aleatorias dentro de un rango determinado.
  • La función "u.RNDfromCI" generará un valor aleatorio, mientras que "u.SeInDiSp" lo corregirá con el paso dado.

2. Ciclo de movimiento básico si "revision" es igual a "true". El método cumple la lógica básica del movimiento bacteriano. Definición de temperatura "T":

  • Si el valor actual de la función "f_tr" es mejor o igual que el valor "f_pr" anterior, se utilizará la temperatura inicial "T0".
  • En caso contrario, la temperatura se corregirá mediante la función "CalcularCorregidoB", que considerará la diferencia entre el valor actual y el anterior de la función de adaptación. 
  • En la generación del tiempo de desplazamiento "tau": se utilizará una distribución exponencial para generar el tiempo de desplazamiento.
  • Los nuevos ángulos de desplazamiento y la nueva posición se calcularán partiendo de la longitud de desplazamiento "l" y los nuevos ángulos.
  • La nueva posición se ajustará al rango y al paso especificados.
  • Al final del ciclo, se actualizará el valor anterior de la función de aptitud de cada bacteria.

El método Moving implementará la lógica básica de desplazamiento de las bacterias en el espacio de búsqueda, adaptando su comportamiento según los resultados de las iteraciones anteriores. Así, utilizará elementos aleatorios y mecanismos adaptativos para encontrar una solución óptima.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_BCO::Moving ()
{
  //----------------------------------------------------------------------------
  if (!revision)
  {
    for (int i = 0; i < popSize; i++)
    {
      for (int c = 0; c < coords; c++)
      {
        a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
        a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }

    revision = true;
    return;
  }

  //----------------------------------------------------------------------------
  for (int i = 0; i < popSize; i++)
  {
    double f_tr = a [i].f;
    double f_pr = bacterium [i].fPrev;

    double T;
    
    if (f_tr <= f_pr)
    {
      T = T0;
    }
    else
    {
      double b_corr = CalculateCorrectedB (f_tr, f_pr, i);
      
      T = T0 * exp (b_corr * (f_tr - f_pr));
    }

    double tau = GenerateFromExponentialDistribution (T);

    double new_angles [];
    ArrayResize (new_angles, coords - 1);
    CalculateNewAngles (new_angles);

    double l = v * tau;
    double new_position [];
    ArrayResize (new_position, coords);
    CalculateNewPosition (l, new_angles, new_position, a [i].c);

    for (int c = 0; c < coords; c++)
    {
      a [i].c [c] = u.SeInDiSp (new_position [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }

    bacterium [i].fPrev = a [i].f;
  }
}
//——————————————————————————————————————————————————————————————————————————————

El método "Revision" de la clase "C_AO_BCO" se encargará de actualizar la información sobre las mejores soluciones encontradas y la historia de los valores de la función de aptitud de cada bacteria.

1. La variable "ind" se inicializará con el valor "-1". Se usará para almacenar el índice de la bacteria con el mejor valor de función encontrado.

2. Localización del mejor valor de la función:

  • El método iterará todas las bacterias de la población "popSize" y buscará una que tenga un valor de función "f" mayor que el mejor valor actual "fB".
  • Si se encuentra una bacteria con un valor de función superior, "fB" se actualizará y el índice de esa bacteria se almacenará en "ind".

3. Si se encuentra una bacteria con el índice "ind" (es decir, "ind" no es igual a "-1"), las coordenadas de esta bacteria se copiarán en el array "cB", que representará las coordenadas de la mejor solución actual.

4. La historia de valores de función se actualizará para cada bacteria. El método recorrerá cada elemento de "fHistory", desplazando los valores una posición a la izquierda para hacer sitio al nuevo valor. Al final de cada iteración, el valor de aptitud actual "a[i].f" de cada bacteria se escribirá en el último elemento del array "fHistory".

Así, el método revision cumplirá dos funciones principales:

  • La actualización del mejor valor de la función de aptitud y las coordenadas correspondientes.
  • La actualización de la historia de valores de la función de aptitud de cada bacteria, lo cual permitirá monitorear los cambios en su estado a lo largo de su historia de desplazamientos. 
//——————————————————————————————————————————————————————————————————————————————
void C_AO_BCO::Revision ()
{
  int ind = -1;

  for (int i = 0; i < popSize; i++)
  {
    if (a [i].f > fB)
    {
      fB = a [i].f;
      ind = i;
    }
  }

  if (ind != -1)
  {
    ArrayCopy (cB, a [ind].c, 0, 0, WHOLE_ARRAY);
  }

  for (int i = 0; i < popSize; i++)
  {
    for (int j = 1; j < hs; j++)
    {
      bacterium [i].fHistory [j - 1] = bacterium [i].fHistory [j];
    }

    bacterium [i].fHistory [hs - 1] = a [i].f;
  }
}
//——————————————————————————————————————————————————————————————————————————————

El método "CalculateAverageDeltaFpr" de la clase "C_AO_BCO" está diseñado para calcular los cambios medios en los valores de la función de aptitud (delta) para una bacteria concreta entre sus dos posiciones vecinas, basándose en la historia de valores de aptitud.

//——————————————————————————————————————————————————————————————————————————————
double C_AO_BCO::CalculateAverageDeltaFpr (int bacteriumIndex)
{
  double sum = 0;

  for (int i = 1; i < hs; i++)
  {
    sum += bacterium [bacteriumIndex].fHistory [i] - bacterium [bacteriumIndex].fHistory [i - 1];
  }

  return sum / (hs - 1);
}
//——————————————————————————————————————————————————————————————————————————————

El método "CalculateNewAngles" de la clase "C_AO_BCO" está diseñado para calcular nuevos ángulos basados en la lógica relacionada con la rotación y la distribución, y realiza las siguientes acciones:

  • Recorre el array en busca de nuevos ángulos y calcula un nuevo valor para cada ángulo.
  • Usa los parámetros que dependen del coseno del ángulo para generar valores que utilizan una distribución gaussiana.
//——————————————————————————————————————————————————————————————————————————————
void C_AO_BCO::CalculateNewAngles (double &angles [])
{
  for (int i = 0; i < coords - 1; i++)
  {
    double theta = CalculateRotationAngle ();
    double mu    = 62 * (1 - MathCos (theta)) * M_PI / 180.0;
    double sigma = 26 * (1 - MathCos (theta)) * M_PI / 180.0;

    angles [i] = u.GaussDistribution (mu, mu - M_PI, mu + M_PI, 8);
  }
}
//——————————————————————————————————————————————————————————————————————————————

El método "CalculateNewPosition" de la clase "C_AO_BCO" está diseñado para calcular las coordenadas de la nueva posición basándose en las coordenadas actuales, los ángulos y el parámetro "l". 

1. Parámetros de entrada del método:

  • l - coeficiente que afecta al cambio de posición.
  • angles [] - array de ángulos que se utilizan para calcular la nueva posición.
  • new_position [] - array donde se escribirán las nuevas coordenadas.
  • current_position [] - array de coordenadas actuales.

2. La primera coordenada "new_position [0]" se calculará como la suma de la coordenada "current_position [0]" actual y el producto de "l" por la diferencia entre "rangeMax [0]" y "rangeMin [0]".

3. A continuación, la primera coordenada se multiplicará por los cosenos de los ángulos del array "angles", empezando por el primero hasta el penúltimo.

4. El resultado se multiplicará por el valor devuelto por la función "RNDdir ()", que generará una dirección aleatoria "-1" o "1".

5. Para cada coordenada "new_position [i]" siguiente, donde "i" estará comprendida entre "1" y "coords - 2", se calculará una nueva posición basada en la posición actual y el seno del ángulo correspondiente.

6. Cada nueva coordenada se multiplicará también por los cosenos de los ángulos desde el índice actual "i" hasta el penúltimo.

7. Dirección aleatoria para el resto de coordenadas, el resultado también se multiplicará por el valor devuelto por "RNDdir ()".

8. Procesamiento de la última coordenada, si el número de coordenadas es superior a 1, se calculará una nueva posición para la última coordenada "new_position [coords - 1]" basada en la posición actual y el seno del último ángulo.

Así, el método "CalculateNewPosition" realizará las siguientes acciones:

  • Calculará las nuevas coordenadas a partir de las coordenadas y ángulos actuales.
  • Considerará el efecto de la dirección aleatoria en cada coordenada.
  • Aplicará las funciones trigonométricas (seno y coseno) para tener en cuenta los ángulos.

Así, este método se utilizará para modelizar el movimiento de las bacterias en el espacio dada su posición actual y unos ángulos determinados.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_BCO::CalculateNewPosition (double l, const double &angles [], double &new_position [], const double &current_position [])
{
  new_position [0] = current_position [0] + l * (rangeMax [0] - rangeMin [0]);
 
  for (int k = 0; k < coords - 1; k++)
  {
    new_position [0] *= MathCos (angles [k]);
  }
 
  new_position [0] *= RNDdir ();

  for (int i = 1; i < coords - 1; i++)
  {
    new_position [i] = current_position [i] + l * MathSin (angles [i - 1]) * (rangeMax [0] - rangeMin [0]);
    
    for (int k = i; k < coords - 1; k++)
    {
      new_position [i] *= MathCos (angles [k]);
    }
    
    new_position [i] *= RNDdir ();
  }

  if (coords > 1)
  {
    new_position [coords - 1] = current_position [coords - 1] + l * MathSin (angles [coords - 2]);
  }
 
}
//——————————————————————————————————————————————————————————————————————————————

A continuación, describiremos brevemente el método "RNDdir" de la clase "C_AO_BCO", que está diseñado para generar la dirección aleatoria que puede tomar uno de dos valores: -1 o 1.

//——————————————————————————————————————————————————————————————————————————————
int C_AO_BCO::RNDdir ()
{
  if (u.RNDbool () < 0.5) return -1;
 
  return 1;
}
//——————————————————————————————————————————————————————————————————————————————

Vamos a repasar brevemente los métodos de la clase "C_AO_BCO".

El método "GenerateFromExponentialDistribution" ejecutará:

  • la generación de un número aleatorio usando distribución exponencial con el parámetro "T".
  • después utilizará un número aleatorio del intervalo (0, 1), calculará su logaritmo y lo multiplicará por "-T".
  • así obtendremos un número aleatorio distribuido según la ley exponencial.

 El método "CalculateCorrectedB" ejecutará:

  • el cálculo del valor "b" ajustado a partir de la diferencia entre "f_tr" y "f_pr" (aptitud actual y anterior).
  • luego calculará la diferencia entre "f_tr" y "f_pr", obtendrá el valor medio de la bacteria y retornará el valor "b" ajustado en función de éste.
//——————————————————————————————————————————————————————————————————————————————
double C_AO_BCO::GenerateFromExponentialDistribution (double T)
{
  return -T * MathLog (u.RNDprobab ());
}

double C_AO_BCO::CalculateCorrectedB (double f_tr, double f_pr, int bacteriumIndex)
{
  double delta_f_tr = f_tr - f_pr;
  double delta_f_pr = CalculateAverageDeltaFpr (bacteriumIndex);

  if (MathAbs (delta_f_pr) < DBL_EPSILON)
  {
    return b;
  }
  else
  {
    return b * (1 / (MathAbs (delta_f_tr / delta_f_pr) + 1) + 1 / (MathAbs (f_pr) + 1));
  }
}
//——————————————————————————————————————————————————————————————————————————————

El método "CalculateRotationAngle" de la clase "C_AO_BCO" será el último. El método calculará el ángulo de rotación basándose en los parámetros dados y retornará el valor en radianes.

//——————————————————————————————————————————————————————————————————————————————
double C_AO_BCO::CalculateRotationAngle ()
{
  double cos_theta = MathExp (-tau_c / T0);
  return MathArccos (cos_theta);
}
//——————————————————————————————————————————————————————————————————————————————

Vamos a poner a prueba la versión original del algoritmo y a ver los resultados:

BCO|Bacterial Chemotaxis Optimization|50.0|10.0|1.0|0.5|1.0|1.0|
=============================
5 Hilly's; Func runs: 10000; result: 0.42924491510564006
25 Hilly's; Func runs: 10000; result: 0.282259866768426
500 Hilly's; Func runs: 10000; result: 0.2515386629014219
=============================
5 Forest's; Func runs: 10000; result: 0.2476662231845009
25 Forest's; Func runs: 10000; result: 0.17824381036550777
500 Forest's; Func runs: 10000; result: 0.15324081202657283
=============================
5 Megacity's; Func runs: 10000; result: 0.2430769230769231
25 Megacity's; Func runs: 10000; result: 0.11415384615384619
500 Megacity's; Func runs: 10000; result: 0.09444615384615461
=============================
All score: 1.99387 (22.15%)

Los resultados obtenidos son tan poco significativos que no tiene sentido incluirlos en la tabla de clasificación. Al diseñar el algoritmo y escribir el código según la descripción del texto del autor, hemos tenido que ajustar algunas fórmulas: algunas no tenían sentido matemático (por ejemplo, dividir un número por un vector), mientras que otras degeneraban en valores extremadamente pequeños o extremadamente grandes incomparables con las dimensionalidades del problema, al tiempo que yo imprimía los resultados de las funciones para cada una de las fórmulas presentadas y los analizaba. De esta forma, tal y como lo describen los autores, el algoritmo no puede funcionar en principio.

Además, las operaciones con ángulos que usan una distribución normal en sus cálculos pueden simplificarse enormemente, ya que el significado físico de estas operaciones supone que en cada coordenada uno puede simplemente posponer un nuevo incremento desde la ubicación actual de la bacteria. Por consiguiente, hemos decidido desarrollar nuestra propia implementación de la quimiotaxis bacteriana, adhiriéndonos a los conceptos e ideas básicas del algoritmo en la medida de lo posible.

Así pues, le presentamos el pseudocódigo de nuestra versión del algoritmo:

Inicialización:
1. Crear una población de bacterias popSize
2. Para cada bacteria i:
    Inicializar con un valor cero la historia de los valores de la función objetivo f_history [i] de tamaño hs.
    Fijar el valor inicial f_prev [i] = -DBL_MAX

Ciclo primario:
Hasta que se alcance la condición de parada:

1. Si esta es la primera iteración:
   Para cada bacteria i:
     Inicializar aleatoriamente la posición x [i] en el espacio de búsqueda
     x [i].[j] ∈ [x_min [j], x_max [j]] para cada coordenada j

2. De lo contrario:
   Para cada bacteria i:
     a. Calcular el cambio medio de la función objetivo:
        delta_ave [i] = (1 / (hs - 1)) * sum (f_history [i].[k] - f_history [i].[k-1] for k in 1..hs-1) + epsilon
     
     b. Calcular el cambio relativo de la adaptabilidad:
        delta [i] = 1 - |f (x [i]) - f_prev [i]| / delta_ave [i]
        delta [i] = max (delta [i], 0.0001)
     
     c. Para cada coordenada j:
        Con una probabilidad de 0,5:
          dist [j] = (x_max [j] - x_min [j]) * delta [i]
          x [i].[j] = N (x [i].[j], x [i].[j] - dist [j], x [i].[j] + dist [j])
          Limitar x [i].[j] dentro de [x_min [j], x_max [j]]
        De lo contrario:
          x [i].[j] = x_best [j]
     
     d. Actualizar f_prev [i] = f (x [i])

3. Estimar la función objetivo f (x [i]) para cada bacteria

4. Actualizar la mejor solución encontrada:
   Si existe i: f (x [i]) > f (x_best), entonces x_best = x [i]

5. Actualizar la historia de valores de la función objetivo para cada bacteria:
   Desplazar valores en f_history [i]
   Añadir un nuevo valor: f_history [i].[hs - 1] = f (x [i])

Finalización:
Retornar la mejor solución encontrada x_best

Donde:

  • x [i] - posición de la i-ésima bacteria
  • f (x) - función objetivo
  • hs - tamaño de la historia
  • epsilon - constante menor para evitar la división por cero
  • N (μ, a, b) - distribución normal truncada con media μ y límites [a, b]

Así, nuestro pseudocódigo modificado reflejará la estructura básica y la lógica del algoritmo BCO. Vamos a centrarnos en los puntos principales:

  • El algoritmo usa una población de bacterias, cada una de las cuales representará una solución potencial.
  • En cada iteración, las bacterias se desplazarán por el espacio de búsqueda utilizando las información sobre sus resultados anteriores.
  • El movimiento se basará en el cambio relativo de la función objetivo, lo cual permitirá al algoritmo adaptarse al paisaje de optimización.
  • El algoritmo almacenará la historia de los valores de la función objetivo para cada bacteria, que se utilizará para calcular el cambio medio.
  • Con cierta probabilidad, la bacteria se desplazará hacia la mejor solución conocida en lugar de explorar una nueva zona (sería un análogo de la herencia de rasgos en genética).
  • Después de cada movimiento, se evaluarán las nuevas posiciones y se actualizará la mejor solución encontrada.

La versión BCOm combina elementos de búsqueda local (desplazamiento de bacterias individuales) y la búsqueda global (intercambio de información a través de la mejor solución conocida).

Veamos las principales diferencias entre las dos versiones del algoritmo. En primer lugar, hemos simplificado el mecanismo de movimiento de las bacterias, abandonando el complejo sistema con ángulos de rotación y temperatura. En segundo lugar, la nueva versión se basa menos en la historia de cambios para ajustar el comportamiento bacteriano, usando un enfoque más directo para actualizar las posiciones. En lugar de una combinación de las distribuciones exponencial y normal, el nuevo algoritmo usa una distribución normal para actualizar las coordenadas. La consecuencia de los cambios introducidos ha sido la reducción a uno del número de parámetros (excluido el tamaño de la población) que controlan el comportamiento del algoritmo, lo cual ha cambiado la forma de afinar el algoritmo y ha simplificado el control del proceso de optimización.

En general, el nuevo pseudocódigo implica cálculos más sencillos en cada paso de optimización, lo que también debería tener un efecto positivo en la velocidad del algoritmo (la versión original realizaba múltiples cálculos del seno y el coseno de los ángulos de rotación para cada coordenada de cada bacteria). Nuestro enfoque establece un equilibrio ligeramente diferente entre la exploración de un nuevo espacio de soluciones y la explotación de las buenas soluciones ya encontradas.

Estos cambios en la lógica han dado lugar a un algoritmo más sencillo y presumiblemente (a la espera de los resultados de las pruebas) más eficaz. Vamos a escribir el código.

Dejaremos inalterada la estructura "S_BCO_Bacteria" que representa a cada bacteria. Está diseñada para almacenar información sobre la bacteria y su historia de valores de función objetivo.

En el método "Init" de la clase "C_AO_BCOm", responsable de la inicialización de los parámetros del algoritmo, añadiremos la definición de la distancia de movimiento admisible para cada coordenada.

Así, el método "Init" de la clase "C_AO_BCOm" se encargará de inicializar los parámetros del algoritmo de optimización. Luego comprobaremos las condiciones de inicialización estándar, crearemos los arrays necesarios y los rellenaremos con valores.

//——————————————————————————————————————————————————————————————————————————————
bool C_AO_BCOm::Init (const double &rangeMinP  [], //minimum search range
                      const double &rangeMaxP  [], //maximum search range
                      const double &rangeStepP [], //step search
                      const int     epochsP = 0)   //number of epochs
{
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //----------------------------------------------------------------------------
  ArrayResize (bacterium, popSize);
  for (int i = 0; i < popSize; i++) bacterium [i].Init (coords, hs);

  ArrayResize (allowedDispl, coords);
  for (int c = 0; c < coords; c++) allowedDispl [c] = rangeMax [c] - rangeMin [c];

  return true;
}
//——————————————————————————————————————————————————————————————————————————————

Ahora analizaremos el método "Moving" de la clase "C_AO_BCOm", que se encarga de desplazar las bacterias en el espacio de búsqueda. Vamos a desglosarla pieza por pieza.

1. En la primera iteración, las acciones del método no han cambiado: se inicializarán las coordenadas de las bacterias con valores aleatorios dentro de un rango dado.

2. El algoritmo básico desplazamiento: se declararán las variables para almacenar los valores como "Δ", "ΔAve", y "dist" y para cada individuo de la población:

  • El valor medio "ΔAve" se calculará con la función "CalculateAverageDeltaFpr (i)".
  • El cambio relativo "Δ" se calculará y se utilizará para determinar el grado de cambio en la posición de la bacteria.
  • Si "Δ" es demasiado pequeño, se fijará en 0,0001.

3. Cambio de coordenadas:

  • Para cada coordenada, se probará una probabilidad aleatoria (50%).
  • Si se cumple la condición, se calculará la distancia "dist", que depende de "allowedDispl [c]" y "Δ".
  • El nuevo valor "x" se calculará mediante la función "GaussDistribution", teniendo en cuenta los límites "xMin" y "xMax".
  • Si "x" está fuera de rango, se corregirá utilizando "RNDfromCI".
  • Por último, el nuevo valor de las coordenadas se almacenará teniendo en cuenta el paso "rangeStep".

4. Se conservará el valor anterior de la función de aptitud "f" para cada individuo del array "bacteria". Se utilizarán los arrays "a" y "bacteria". Las funciones "RNDfromCI", "SeInDiSp", "GaussDistribution" se encargarán de generar números aleatorios y distribuciones, así como de normalizar los valores de las coordenadas.

Así, la función "Moving ()" se encargará de inicializar y actualizar la posición de los individuos en la población dentro del algoritmo. Este utilizará probabilidades y distribuciones aleatorias para controlar el movimiento de las bacterias. Sin embargo, la diferencia clave con respecto a la versión original será la forma más sencilla y eficaz de realizar el gradiente de la función de aptitud. Cuando la diferencia de estado de la bacteria en el paso actual disminuya en comparación con el paso anterior, su movimiento se acelerará. Por el contrario, la bacteria se ralentizará cuando se encuentra en un entorno externo favorable. Esto resulta contrario al comportamiento natural de las bacterias, que entran en un estado de depresión y anabiosis en un entorno agresivo.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_BCOm::Moving ()
{
  //----------------------------------------------------------------------------
  if (!revision)
  {
    for (int i = 0; i < popSize; i++)
    {
      for (int c = 0; c < coords; c++)
      {
        a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
        a [i].c [c] = u.SeInDiSp  (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }

    revision = true;
    return;
  }

  //----------------------------------------------------------------------------
  double x    = 0.0;
  double xMin = 0.0;
  double xMax = 0.0;
  double Δ    = 0.0;
  double ΔAve = 0.0;
  double dist = 0.0;

  for (int i = 0; i < popSize; i++)
  {
    ΔAve = CalculateAverageDeltaFpr (i) + DBL_EPSILON;

    Δ = fabs (a [i].f - bacterium [i].fPrev) / ΔAve;

    Δ = 1.0 - Δ;

    if (Δ < 0.0001) Δ = 0.0001;

    for (int c = 0; c < coords; c++)
    {
      if (u.RNDprobab () < 0.5)
      {
        dist = allowedDispl [c] * Δ;

        x    = a [i].c [c];
        xMin = x - dist;
        xMax = x + dist;

        x = u.GaussDistribution (x, xMin, xMax, 8);

        if (x > rangeMax [c]) x = u.RNDfromCI (xMin, rangeMax [c]);
        if (x < rangeMin [c]) x = u.RNDfromCI (rangeMin [c], xMax);

        a [i].c [c] = u.SeInDiSp (x, rangeMin [c], rangeMax [c], rangeStep [c]);
      }
      else a [i].c [c] = cB [c];
    }

    bacterium [i].fPrev = a [i].f;
  }
}
//——————————————————————————————————————————————————————————————————————————————

El método "Revision ()" de la clase "C_AO_BCOm", encargado de actualizar la información sobre la población y la historia de valores de la función objetivo, no ha cambiado.


Resultados de las pruebas

Veamos ahora cómo funciona la nueva versión del algoritmo BCOm:

BCO|Bacterial Chemotaxis Optimization|50.0|10.0|
=============================
5 Hilly's; Func runs: 10000; result: 0.759526049526603
25 Hilly's; Func runs: 10000; result: 0.6226756163411526
500 Hilly's; Func runs: 10000; result: 0.31483373090540534
=============================
5 Forest's; Func runs: 10000; result: 0.8937814268120954
25 Forest's; Func runs: 10000; result: 0.6133909133246214
500 Forest's; Func runs: 10000; result: 0.22541842289630293
=============================
5 Megacity's; Func runs: 10000; result: 0.653846153846154
25 Megacity's; Func runs: 10000; result: 0.42092307692307684
500 Megacity's; Func runs: 10000; result: 0.14435384615384755
=============================
All score: 4.64875 (51.65%)

Probablemente, la experiencia acumulada y una mirada crítica a la información disponible sobre la versión original hayan tenido su efecto, y la nueva versión del algoritmo haya demostrado ser más potente en la aplicación práctica que la original.

En la visualización del rendimiento del algoritmo BCOm, podemos observar una buena exploración de partes significativas del hiperespacio, lo cual indica una gran capacidad para explorar la superficie de la función que se está optimizando.

Hilly

BCOm en la función de prueba Hilly

Forest

BCOm en la función de prueba Forest

Megacity

BCOm sobre la función de prueba Megacity

Al final de las pruebas, el algoritmo ha ocupado un estable 17º puesto en la clasificación general de los algoritmos de optimización.

AO Description HillyHilly final ForestForest final Megacity (discrete)Megacity final Final result % de MAX
10 p (5 F)50 p (25 F)1000 p (500 F)10 p (5 F)50 p (25 F)1000 p (500 F)10 p (5 F)50 p (25 F)1000 p (500 F)
1ANSacross neighbourhood search0,949480,847760,438572,235811,000000,923340,399882,323230,709230,634770,230911,574916,13468,15
2CLAcode lock algorithm0,953450,871070,375902,200420,989420,917090,316422,222940,796920,693850,193031,683806,10767,86
3AMOmanimal migration ptimization M0,903580,843170,462842,209590,990010,924360,465982,380340,567690,591320,237731,396755,98766,52
4(P+O)ES(P+O) evolution strategies0,922560,881010,400212,203790,977500,874900,319452,171850,673850,629850,186341,490035,86665,17
5CTAcomet tail algorithm0,953460,863190,277702,094350,997940,857400,339492,194840,887690,564310,105121,557125,84664,96
6SDSmstochastic diffusion search M0,930660,854450,394762,179880,999830,892440,196192,088460,723330,611000,106701,441035,70963,44
7ESGevolution of social groups0,999060,796540,350562,146161,000000,828630,131021,959650,823330,553000,047251,423585,52961,44
8SIAsimulated isotropic annealing0,957840,842640,414652,215130,982390,795860,205071,983320,686670,493000,090531,270205,46960,76
9ACSartificial cooperative search0,755470,747440,304071,806981,000000,888610,224132,112740,690770,481850,133221,305835,22658,06
10ASOanarchy society optimization0,848720,746460,314651,909830,961480,791500,238031,991010,570770,540620,166141,277525,17857,54
11TSEAturtle shell evolution algorithm0,967980,644800,296721,909490,994490,619810,227081,841390,690770,426460,135981,253225,00455,60
12DEdifferential evolution0,950440,616740,303081,870260,953170,788960,166521,908650,786670,360330,029531,176534,95555,06
13CROchemical reaction optimisation0,946290,661120,298531,905930,879060,584220,211461,674730,758460,426460,126861,311784,89254,36
14BSAbird swarm algorithm0,893060,649000,262501,804550,924200,711210,249391,884790,693850,326150,100121,120124,80953,44
15HSharmony search0,865090,687820,325271,878180,999990,680020,095901,775920,620000,422670,054581,097254,75152,79
16SSGsaplings sowing and growing0,778390,649250,395431,823080,859730,624670,174291,658690,646670,441330,105981,193984,67651,95
17BCOmbacterial chemotaxis optimization M0,759530,622680,314831,697040,893780,613390,225421,732590,653850,420920,144351,219124,64951,65
18(PO)ES(PO) evolution strategies0,790250,626470,429351,846060,876160,609430,195911,681510,590000,379330,113221,082554,61051,22
19TSmtabu search M0,877950,614310,291041,783300,928850,518440,190541,637830,610770,382150,121571,114494,53650,40
20BSObrain storm optimization0,937360,576160,296881,810410,931310,558660,235371,725340,552310,290770,119140,962224,49849,98
21WOAmwale optimization algorithm M0,845210,562980,262631,670810,931000,522780,163651,617430,663080,411380,113571,188034,47649,74
22AEFAartificial electric field algorithm0,877000,617530,252351,746880,927290,726980,180641,834900,666150,116310,095080,877544,45949,55
23ACOmant colony optimization M0,881900,661270,303771,846930,858730,586800,150511,596040,596670,373330,024720,994724,43849,31
24BFO-GAbacterial foraging optimization - ga0,891500,551110,315291,757900,969820,396120,063051,428990,726670,275000,035251,036924,22446,93
25ABHartificial bee hive algorithm0,841310,542270,263041,646630,878580,477790,171811,528180,509230,338770,103970,951974.12745,85
26ASBOadaptive social behavior optimization0,763310,492530,326191,582020,795460,400350,260971,456770,264620,171690,182000,618313.65740,63
27MECmind evolutionary computation0,695330,533760,326611,555690,724640,330360,071981,126980,525000,220000,041980,786983,47038,55
28IWOinvasive weed optimization0,726790,522560,331231,580580,707560,339550,074841,121960,423330,230670,046170,700173,40337,81
29Micro-AISmicro artificial immune system0,795470,519220,308611,623300,729560,368790,093981,192330,376670,158670,028020,563353,37937,54
30COAmcuckoo optimization algorithm M0,758200,486520,313691,558410,740540,280510,055991,077040,505000,174670,033800,713473,34937,21
31SDOmspiral dynamics optimization M0,746010,446230,296871,489120,702040,346780,109441,158260,428330,167670,036630,632633,28036,44
32NMmNelder-Mead method M0,738070,505980,313421,557470,636740,283020,082211,001970,446670,186670,040280,673623,23335,92
33FAmfirefly algorithm M0,586340,472280,322761,381380,684670,374390,109081,168140,286670,164670,047220,498553,04833,87
34GSAgravitational search algorithm0,647570,491970,300621,440160,539620,363530,099451,002600,326670,122000,019170,467832,91132,34
35BFObacterial foraging optimization0,611710,432700,313181,357590,544100,215110,056760,815970,421670,138000,031950,591622,76530,72
36ABCartificial bee colony0,633770,424020,308921,366710,551030,218740,056230,826000,340000,142000,031020,513022,70630,06
37BAbat algorithm0,597610,459110,352421,409150,403210,193130,071750,668100,210000,101000,035170,346172,42326,93
38AAAalgae adaptive algorithm0,500070,320400,255251,075720,370210,222840,167850,760890,278460,148000,097550,524022,36126,23
39SAsimulated annealing0,557870,421770,315491,295130,349980,152590,050230,552800,311670,100330,028830,440832,28925,43
40IWDmintelligent water drops M0,545010,378970,301241,225220,461040,147040,043690,651770,258330,097000,023080,378422,25525,06
41PSOparticle swarm Optimization0,597260,369230,299281,265770,372370,163240,070100,605720,256670,080000,021570,358232,23024,77
42Boidsboids algorithm0,433400,305810,254250,993460,357180,201600,157080,715860,278460,142770,098340,519572,22924,77
43MAmonkey algorithm0,591070,426810,318161,336040,311380,140690,066120,518190,228330,085670,027900,341902,19624,40
44SFLshuffled frog-leaping0,539250,358160,298091,195510,371410,114270,040510,526180,271670,086670,024020,382352,10423,38
45FSSfish school search0,556690,399920,311721,268330,310090,118890,045690,474670,211670,076330,024880,312882,05622,84




Conclusiones

Hoy hemos presentado al lector las versiones original BCO y modificada BCOm del algoritmo. La versión original, recreada a partir de información disponible de código abierto, está sobrecargada con pesados cálculos trigonométricos y con débiles propiedades de búsqueda, y muestra "meteduras de pata" matemáticas. Por ello, hemos tenido que replantearnos el algoritmo completo y analizar en profundidad la estrategia de búsqueda y crear una nueva versión modificada. Los cambios en la lógica del algoritmo han dado lugar a cálculos más sencillos en cada paso de optimización, lo cual ha tenido un efecto positivo en la velocidad de ejecución del código. El nuevo algoritmo también establece un equilibrio diferente entre la exploración de un nuevo espacio de soluciones y la explotación de las buenas soluciones ya encontradas.

Aunque el planteamiento actualizado resulta ligeramente contrario al comportamiento natural de las bacterias, ha demostrado ser muy eficaz en aplicaciones prácticas. La visualización del rendimiento del algoritmo ha mostrado su capacidad para explorar en profundidad regiones significativas del hiperespacio, lo que también indica su mejor capacidad de exploración. Al final, la nueva versión del algoritmo ha resultado más potente y eficiente que la original.


Tab

Figura 1. Gradación por colores de los algoritmos según sus respectivas pruebas. Los resultados superiores o iguales a 0,99 aparecen resaltados en blanco.

chart

Figura 2. Histograma de los resultados de las pruebas de los algoritmos (en una escala de 0 a 100, cuanto mayor, mejor,

donde 100 es el máximo resultado teórico posible, el script para calcular la tabla de clasificación está en el archivo)


Ventajas e inconvenientes del algoritmo BCOm:

Ventajas:

  1. Es rápido.
  2. Autoadaptable.
  3. Posee una buena escalabilidad.
  4. Solo un parámetro externo.

Desventajas:

  1. Gran dispersión de resultados en funciones de pequeña dimensionalidad.

Adjuntamos al artículo un archivo con las versiones actuales de los códigos de los algoritmos. El autor de este artículo no se responsabiliza de la exactitud absoluta de la descripción de los algoritmos canónicos, muchos de ellos han sido modificados para mejorar las capacidades de búsqueda. Las conclusiones y juicios presentados en los artículos se basan en los resultados de los experimentos realizados.

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/15711

Archivos adjuntos |
BCOm.zip (36.35 KB)
El enfoque cuantitativo en la gestión de riesgos: Aplicación de un modelo VaR para la optimización de portafolios multidivisa con Python y MetaTrader 5 El enfoque cuantitativo en la gestión de riesgos: Aplicación de un modelo VaR para la optimización de portafolios multidivisa con Python y MetaTrader 5
Este artículo revelará el potencial del modelo Value at Risk (VaR) para optimizar un portafolio multidivisa. Usando el poder de Python y la funcionalidad de MetaTrader 5, hoy demostraremos cómo implementar el análisis VaR para la asignación eficiente de capital y la gestión de posiciones. Desde los fundamentos teóricos hasta la aplicación práctica, el artículo abarcará todos los aspectos de la aplicación de uno de los sistemas de cálculo del riesgo más sólidos, el VaR, a la negociación algorítmica.
Algoritmo de Algas Artificiales (Artificial Algae Algorithm, AAA) Algoritmo de Algas Artificiales (Artificial Algae Algorithm, AAA)
El artículo considera el Algoritmo de Algas Artificiales (Artificial Algae Algorithm, AAA) basado en procesos biológicos característicos de las microalgas. El algoritmo incluye movimiento en espiral, proceso evolutivo y adaptación, lo que le permite resolver problemas de optimización. El artículo analiza en profundidad los principios de funcionamiento del AAA y su potencial en la modelización matemática, destacando la conexión entre la naturaleza y las soluciones algorítmicas.
Ciclos y Forex Ciclos y Forex
Los ciclos son de gran importancia en nuestras vidas. El día y la noche, las estaciones, los días de la semana y muchos otros ciclos de distinta naturaleza están presentes en la vida de cualquier persona. En este artículo, consideraremos los ciclos en los mercados financieros.
Redes neuronales en el trading: Un método complejo de predicción de trayectorias (Traj-LLM) Redes neuronales en el trading: Un método complejo de predicción de trayectorias (Traj-LLM)
En este artículo, me gustaría presentarles un interesante método de predicción de trayectorias desarrollado para resolver problemas en el campo de los movimientos de vehículos autónomos. Los autores del método combinaron los mejores elementos de varias soluciones arquitectónicas.