• No se han encontrado resultados

Evolución Gramatical y Enjambres de Gramáticas

La Evolución Gramatical fue propuesta por Ryan, Collins y O’Neill [Ryan et al., 1998] en 1998. Surge como un algoritmo evolutivo capaz de generar automáticamente programas escritos en un lenguaje de pro- gramación. El proceso de generación de programas se divide en la evolución de los genotipos de longitud variable y su correspondiente transformación en los fenotipos, que son los programas propiamente dichos. Pa- ra realizar esta conversión, cada genotipo representa una secuencia de elecciones en las reglas de producción de una gramática libre de contexto representada en la notación de Backus-Naur (BNF). Este procedimiento de generación de programas asegura que siempre son sintácticamente correctos.

La Evolución Gramatical se desarrolla en dos partes, de forma similar a los algoritmos genéticos. En primer lugar, existen un conjunto de genotipos, que son evolucionados (como en los algoritmos genéticos). Estos genotipos generan nuevos genotipos, que son transformados en sus correspondientes fenotipos; los cuales son evaluados, asignando un fitness a cada uno de los genotipos. En base a esto, se siguen produciendo nuevas poblaciones.

El genotipo, que es transformado en un fenotipo, está formado por codones (de forma similar a como en los algoritmos genéticos eran genes). Desde el punto de vista biológico, la información genética del ARN es generada a partir de las4bases nitrogenadas: Adenina, Citosina, Guanina y Uracilo. Estas bases se agrupan

de tres en tres, dando lugar a los codones, que se encargan de codificar un aminoácido o los símbolos de comienzo y parada. En el código genético, cada aminoácido está codificado por un codón o varios codones. En total hay 64 codones que codifican 20 aminoácidos y 3 señales de parada. Esto hace que el código sea redundante, lo que se denomina código degenerado, porque hay varios codones diferentes que codifican el mismo aminoácido. El proceso de transformación del genotipo en fenotipo en la Evolución de Gramáticas simula el fenómeno biológico conocido como la biosíntesis de proteínas. En [Ryan et al., 1998] se propone utilizar 8 bits para cada codón, si bien, por lo general, el número de opciones para cada símbolo es menor. Por ello, el código es también degenerado. Este fenómeno permite la ocurrencia de mutaciones silenciosas, que son aquellas que no tienen efecto en el fenotipo del individuo, y que son la base de la teoría neutral de la evolución que formuló Kimura [Kimura, 1985].

El fenotipo es un programa que es generado a través del genotipo por la elección de las reglas de producción de una gramática libre de contexto. Una gramática formal (G) es una tupla(N,T,P,S), donde

N es un conjunto de símbolos no terminales, los cuales no aparecen en las cadenas formadas por G. T es un conjunto de símbolos terminales, tal que TTN

= ;.

P es un conjunto de reglas de producción, donde cada regla es(TSN)N(TSN)

→(TSN)∗, donde

es el operador de Kleene. SN es el símbolo inicial.

En las gramáticas de contexto libre (según la Jerarquía de Chomsky), las reglas de producción son de la forma:

N(T[

N)∗

La Backus Normal Form o Backus-Naur Form (BNF) es una técnica de notación para gramáticas libres de contexto, que frecuentemente es usada para describir los lenguajes de programación. Como no podría ser de otra forma, la Evolución Gramatical utiliza una representación BNF para realizar la conversión del genotipo al fenotipo.

La conversión de un genotipo al fenotipo correspondiente se realiza transformando los codones en reglas de producción, mediante una derivación por la izquierda. Se empieza por el símbolo inicial de la gramática, y se va aplicando las producciones indicadas para sus no terminales, de izquierda a derecha. Cuando sobre un símbolo no terminal se pueden aplicar varias reglas de transformación, se recurre a un codón (el siguiente del genotipo) para seleccionar cual regla utilizar. Un codón representa la elección, pero es posible que este

valor sea superior al número de opciones. Por ello, se utiliza el módulo de la división del valor de codón entre el número posible de reglas de transformación. Esto hace que varios valores del codón generen el mismo fenotipo, y es conocido como código degenerado. Si solo existiera una regla, no se utilizaría ningún codón (como se muestra en las líneas 6 y 11 del cuadro 6.4). Si se utilizan todos los codones y no se ha finalizado el programa, se presentan dos alternativas. Descartar el programa, asignándolo el peor valor a su fitness, para que el proceso evolutivo lo elimine; o utilizar la técnica conocida como wrapping. El wrapping consiste en reutilizar los codones para continuar el proceso de traducción. Así, una vez utilizado el último codón, se vuelve a utilizar el primero, considerando el genotipo como una cadena de codones circular. Este operador se inspira en el mecanismo de solapamiento de genes que ocurre en algunos organismos, como las bacterias, según ha descrito Lewin [Lewin, 1999]. En este caso, se debe indicar cuantas veces se va a utilizar el genotipo, para evitar bucles permanentes. Si alcanzada esta reutilización no se ha generado un programa (todo símbolos terminales), se asigna al individuo el peor valor del fitness. Un ejemplo del proceso de transformación de genotipo a fenotipo se puede encontrar en el cuadro 6.4, donde el genotipo del cuadro 6.3 se transforma en fenotipo según la gramática siguiente:

G=(N,T,P,S) N={exp,pr e_op,op,v ar} T={Si n,C os,Tan,Log,+,, /,,x}

S=<expr>

P se representa como:

<expr>

::= <expr><op><expr>

|

(<expr><op><expr>)

|

<pr e_op>(<expr>)

|

<v ar>

<op>

::= +

|

|

/

|

<pr e_op>

::=

Si n

|

C os

|

Tan)

|

Log

<v ar>

::=

x

El genotipo estará formado por codones, y estos por un número de bits que depende de la gramática a utilizar. En [Ryan et al., 1998] se propone utilizar 8 bits por codón, pero el tamaño del codón solo debe cumplir que sea igual o superior al número máximo de reglas de transformación de cada no terminal. Por lo general, las representaciones de los codones se suelen realizar utilizando el número entero, y no su secuencia de bits.

220 202 17 184 107 95 218 127 171 246 55

Cuadro 6.3: Ejemplo de genotipo, representado cada codón de 8 bits como un número entero.

Fenotipo

Codón utilizado transformación

seleccionada

<expr>

220

0

<expr><op><expr>

202

2

<pr e_op>(<expr)<op><expr>

17

1

C os(<expr>)<op><expr>

184

0

C os(<expr><op><expr>)<op><expr>

107

3

C os(<v ar

><op><expr>)<op><expr>

0

C os(x<op><expr>)<op><expr>

95

3

C os(x∗ <expr>)<op><expr>

218

2

C os(x∗ <pr e_op>(<expr>))<op><expr>

127

3

C os(xLog(<expr>))<op><expr>

171

3

C os(xLog(<v ar>))<op><expr>

0

C os(xLog(x))<op><expr>

246

2

C os(xLog(x))/<expr>

55

3

C os(xLog(x))/<expr>

0

C os(xLog(x))/x

Cuadro 6.4: Proceso de transformación del genotipo del cuadro 6.3 con la gramática indicada.

El proceso completo de la Evolución de las Gramáticas, consiste en diversas etapas, que son similares a cualquiera de las metaheurísticas de la computación evolutiva. En primer lugar, se inicia una población de genotipos, de forma aleatoria. Cada genotipo estará formado por un número determinado de codones, siendo por tanto una ristra binaria. A partir de cada genotipo, con el algoritmo de transformación a fenotipos, se genera un programa que será una solución del problema planteado. Se calcula el fitness del programa (fenotipo), y se asigna su valor al genotipo correspondiente. A partir de los genotipos creados y su valor de adaptación, comienza el proceso de evolución, en base a operadores evolutivos (en la sección de algoritmos genéticos están descritos algunos de estos), para generar una nueva población. Esta nueva población de genotipos, se convierte en programas (fenotipos) y son nuevamente evaluados. Se repite este proceso un número determinado de veces, o hasta que se encuentre una solución aceptable.

Para la generación de una nueva población, se utilizan los algoritmos genéticos ya descritos, debiendo seleccionar de entre las múltiples opciones que presentan. Pero además, existen nuevos operadores genéticos que se aplican sobre los genotipos. En concreto, en [Ryan et al., 1998] se propone la poda y la duplicación. Sobre el genotipo es posible duplicar ciertos codones (o bits) que son colocados o bien inmediatamente después de sus originales, o bien, al final del genotipo. De forma similar, es posible que una cadena sea cortada por cierto lugar, utilizando una de las partes y descartando la otra. Estos operadores provocan que los genotipos varíen de longitud, aunque en su origen todos tenían la misma longitud. Es el fenómeno de cromosomas de diferente longitud que utilizan los algoritmos genéticos. En el caso de la Evolución de las Gramáticas, es especialmente útil, porque no se conoce cuantos codones serán necesarios para generar el fenotipo, porque dependiendo de los valores contenidos (y de la gramática) serán necesarios unos u otros.

Por ello, un genotipo puede contener codones que no sean necesarios para la generación del fenotipo. A estos codones se los denominan intrones. Debido a la existencia de este tipo de codones, y con el fin de evitar que los padres e hijos generen el mismo fenotipo, dando lugar a un estancamiento local, para el proceso de recombinación se utilizan los codones efectivos. Además, sobre cada uno de los padres el punto de corte de la cadena se realiza en un lugar diferente. Otro problema asociado al uso de codones es que los distintos operadores operan a nivel de bits (y no de codones) pudiéndose dar casos de cadenas que no contengan un número exacto de codones. Por ello, se suele utilizar los operadores a nivel de codón, y no a nivel de bits. Esto va en la misma línea de tratar el genotipo como una secuencia de números, y no como una ristra de bits (aunque ésta sea la representación interna).

Aunque la Evolución Gramatical utiliza como método de búsqueda de la solución un algoritmo evolu- tivo, generalmente un algoritmo genético, O’Sullivan [O’Sullivan and Ryan, 2002] propone que sería válido cualquier método de búsqueda capaz de trabajar con cadenas binarias o enteras de longitud variable. En esta línea de investigación, O’Neill en [O’Neill and Brabazon, 2004] propone el uso de PSO como método de búsqueda, y denominan a la metaheurística completa como Enjambres de Gramáticas (Grammatical Swarm), obteniendo unos resultados similares a los de la Evolución Gramatical. En este caso, el genotipo es generado por un PSO multidimensional (tantos como codones se utilicen), estando cada dimensión limitada a valores entre 0 y 255, utilizándose solo números enteros para la conversión del genotipo al fenotipo (aunque PSO opera con números decimales, se hace un redondeo para obtener números enteros).

Además de excelentes resultados de investigación, como los mostrados en [Cruz, 2010], existen muchos ejemplos del uso de la Evolución Gramatical, aplicada no solo a la creación de programas, sino utilizada como técnica de resolución de problemas. Por citar solo alguno de los ejemplos, en [Colmenar et al., 2010] se utiliza la Evolución Gramatical para generar gestores de memoria dinámica personalizados, que consiguen mejores resultados que los orientados al propósito general. En [Ortega et al., 2003] se utiliza la Evolución de Gramáticas para generar fractales, mientras que en [Cebrián et al., 2009] se utiliza para la generación de herramientas de búsqueda de plagio en las prácticas universitarias. Pero además de las aplicaciones realizadas con las Gramáticas Evolutivas, hay un sector que se dedica al desarrollo y mejora de la potencialidad de esta técnica. En [Echeandia et al., 2005] se utilizan las Gramáticas Evolutivas no sobre gramáticas libres de con- texto, sino sobre gramáticas atributivas, dando lugar a una mejora semántica en los programas desarrollados por esta metaheurística.

Evolución Gramatical con reescritura de gramáticas

Para la distribución de membranas en diferentes procesadores, con el fin de mejorar los tiempos de evolución de los P sistemas, se están utilizando diversas técnicas. Una de ellas, como se ha descrito, es la Evolución Gramatical. Centrándose en el problema de distribución inicial de membranas en procesadores, para la arquitectura P2P, y que según se ha descrito, consiste en minimizar la fórmula 5.1, la metaheurística de Evolución Gramatical, debe ser modificada. Desde aquí se propone el uso de la Evolución Gramatical con reescritura de gramáticas, que permitirá generar opciones válidas de distribución, solventando los problemas topológicos del sistema, y la falta de restricción del número de procesadores. El cambio propuesto solo afecta a la gramática a utilizar, siendo el resto de la metaheurística utilizada de forma análoga a lo ya mostrado.

En la arquitectura P2P (y por extensión en HP2P), existe una fuerte restricción sobre dónde se puede colocar una membrana. Así, si la membrana vi se encuentra en el procesador cx, y existe una membrana

vj, (vi,vj)∈E, entonces vj solo podrá situarse en el mismo procesador (cx), o en alguno de los hijos

({cy|cyC/(cx,cy)∈A}). Esta justificación aparece detallada en la explicación del uso de algoritmos genéticos

para la distribución de membranas preservando la topología arbórea. Por supuesto, también podrá incluirse vj en un nuevo procesador, que pasará a ser hijo de cx.

Considerando una gramática que sea una sucesión de elecciones, de forma que el terminal inicial dé lugar a un conjunto de no terminales (uno por cada una de las membranas), y que sobre cada uno de estos no terminales se decida sobre qué procesador se va a ubicar dicha membrana; la gramática necesitaría para cada

11 202 118 185 106 92 216 138 173 246 54

Cuadro 6.5: Genotipo, que utilizando una transformación con reescritura de gramáticas, para

el P sistema de la figura 5.1 da como resultado la distribución de dicha imagen.

membrana la ubicación de la membrana madre, y los procesadores hijos del procesador donde se encuentra la membrana madre. Esta limitación hace que la técnica de Evolución Gramatical no sea utilizable tal y como proponen sus creadores, ya que no se puede representar una gramática en BNF previa, puesto que depende de las elecciones realizadas sobre cada no terminal. Es decir, la gramática será dependiente del genotipo, y será éste el que se encargue de escribir la gramática en base a la selección de reglas que realice.

Se propone por tanto, una gramática que se reescriba a partir de las decisiones tomadas sobre la propia gramática. La gramática estará compuesta por una única regla inicialmente, que contendrá la ubicación de la membrana raíz, y tantos no terminales como membranas contenga la raíz. Cada uno de estos no terminales, generará tantas reglas de transformación como se puedan obtener de la topología del árbol de procesadores en que se está transformando el genotipo. Así, las opciones estarán siempre formadas por un terminal (que representa al procesador donde se ubicará dicha membrana), y un conjunto de no terminales, correspondientes a las membranas que contenga la membrana. Los terminales (cada uno en una regla) se corresponderán al procesador donde se encuentra la membrana madre, a los hijos de este procesador, y a un procesador nuevo. Con esta aplicación de reglas, se puede considerar que el genotipo representa un recorrido de elecciones en preorden sobre la topología arbórea del P sistema. Así, el genotipo será una cadena de longitud fija. No tiene sentido que varíe su longitud, puesto que es necesario un y solo un codón para cada membrana (excepto la raíz). El número de bits (o valor máximo de codón) es desconocido, al no estar descrita la gramática previa a su uso (además de ser diferente para cada genotipo). Existe una cota superior, correspondiente al número de membranas, pero sería complicado que este valor se alcanzara.

Utilizando como ejemplo el P sistema de la figura 5.1, el genotipo del cuadro 6.5 da como resultado la distribución mostrada en dicha imagen.

En concreto, la gramática inicial estaría formada por la regla:

<t0>::=0<t1><t2>

A partir de esta regla, se escribirían las reglas para <t1>, siendo posible ubicar dicha membrana en el mismo procesador, o en uno nuevo:

<t1>::=0<t3> |1<t3>

El valor de genotipo, elegiría la segunda opción. Continuando con la generación de la gramática:

<t3>::=1<t6><t7> |2<t6><t7>

Siendo seleccionada la primera opción. Así se continúa con el proceso, pero hay que remarcar el caso de la membrana 2, donde las opciones se amplían a 3, ya que el procesador (0) donde se encuentra la membrana

madre (0) tiene un procesador hijo (1). Así, para el no terminal2, la gramática será:

<t2>::=0<t4><t5> |1<t4><t5> |3<t4><t5>

<t

0

>

::=

0<t

1

><t

2

>

<t

1

>

::=

0<t

3

> |1<t

3

>

<t

3

>

::=

1<t

6

><t

7

> |2<t

6

><t

7

>

<t

6

>

::=

1|2

<t

7

>

::=

1<t

10

><t

11

> |2<t

10

><t

11

>

<t

10

>

::=

2|3

<t

11

>

::=

2|3

<t

2

>

::=

0<t

4

><t

5

> |1<t

4

><t

5

> |3<t

4

><t

5

>

<t

4

>

::=

0|1|3

<t

5

>

::=

0<t

8

><t

9

> |1<t

8

><t

9

> |3<t

8

><t

9

>

<t

8

>

::=

3|4

<t

9

>

::=

3|4