Showing posts with label recursión. Show all posts
Showing posts with label recursión. Show all posts

Thursday, September 07, 2023

¿Por qué la Naturaleza es recursiva?


 

Cuando somos niños nos enseñan que lo definido no puede estar en la definición. Por ejemplo, si la maestra en turno nos pide que definamos una mesa, no podemos decir: "una mesa es una mesa", porque precisamente, usamos lo definido en la definición. Probablemente cualquier maestro de primaria se molestaría si mencionásemos el aforismo de Gertrude Stein: "Una rosa es una rosa es una rosa es una rosa" (https://es.wikipedia.org/wiki/Rosa_es_una_rosa_es_una_rosa_es_una_rosa). Y esa que lo definido puede estar en la definición. Por ejemplo, en matemáticas tenemos las funciones recursivas. Una de ellas define, por ejemplo el factorial en dos pasos: una condición terminal y la parte que se recurre a sí misma, la función recursiva:

0! = 1

n!= n * (n-1)!

Si calculamos el factorial de forma recursiva, tendremos que 

3! = 3 * 2!

pero no podemos resolver la ecuación porque no sabemos cuánto es 2! Entonces, aplicamos la misma función y hallamos que 

2! = 2 * 1!

De nuevo, no sabemos cuánto es 1! y aplicamos la función:

1! = 1 * 0!

Y de nuevo, requerimos calcular 0! Pero por la definición, sabemos que 0! = 1, por lo que ya tenemos ese valor y podemos regresar y calcular los valores temporales de lo que antes no teníamos el resultado.

En el caso de la función iterativa, básicamente 3! = 3 * 2 * 1 = 6.

Niklaus Wirth -dicen- puso un comentario en su compilador de Pascal, que decía: "iteratum humanum est, recursivitum divinum est". Y aunque el científico suizo siempre ha negado ser el autor de dicha frase, rechazando incluso que la hubiese escrito en el código fuente de su compilador, es claro que la frase tiene sentido. Es mucho más elegante matemáticamente la función recursiva que la iterativa.

Se sabe que toda función recursiva puede expresarse de forma iterativa. El problema con las funciones recursivas es que requieren de más recursos (sobre todo en las computadoras), para resolver los problemas planteados. Por ejemplo, en el caso de la función factorial recursiva, hay que llevar una estructura de datos llamada stack, que permite ir metiendo los datos no resueltos temporalmente en una estructura llamada LIFO (last In, First Out), es decir, el primero que metemos es el último que sacamos. Por ejemplo, imaginemos una caja con libros, sellada en la parte inferior. Si empiezo a poner libros, el último libro apilado en dicha caja es el primero que puedo sacar y el primer libro que metí es el último que puedo sacar. En cambio, las funciones iterativas no requieren de guardar resultados intermedios. En ese sentido son directas.

Si vamos a la Naturaleza, al mundo real, tenemos que Mandelbrot armó toda una nueva teoría (en su momento), una geometría que llamó fractal, donde hay una autosimilitud en diversos fenómenos que vemos en nuestro mundo. Por ejemplo, un árbol se compone de un tronco y ramas, que a su vez son "pequeños árboles" los cuales, también tienen ramas más pequeñas. Los árboles tienen una estructura fractal en donde a diferentes escalas se ve la similitud.


Y este es un ejemplo del fenómeno de la recursión. El árbol se recurre a sí mismo para crear árboles más pequeños. Hay otros ejemplos increíbles, como el romanescu. Jamás había visto semejante vegetal en los supermercados y cuando lo vi, lo compré sin dudarlo. Es impresionante porque es en realidad una imagen tridimensional de un fractal. Este es uno de los ejemplos más llamativos de que la Naturaleza, sí, con N mayúscula, está seriamente involucrada con los fractales, descubiertos, o inventados (¿?) por Mandelbrot. El romanescu es una estructura cónica con protuberancias también cónicas más pequeñas que se forman a su vez de otras estructuras cónicas, y así sucesivamente. Esto se entiende con el término “autosimilitud”.


Pero probablemente donde la recursión quede en manifiesto es en la reproducción de los seres vivos. Cuando se fecunda un óvulo humano, por ejemplo, empieza un proceso de creación de un nuevo ser, definido por el ADN directamente. Esta morfogenesis es un misterio realmente. ¿En qué momento el ADN decide que ya estuvo bien de replicarse para empezar a hacer células específicas para músculos, corazón, cerebro, etcétera, no está nada claro. Los biólogos han estudiado estos mecanismos y parece ser que la Naturaleza hace una especie de "switcheo de genes", el cual apaga o prende algunos de estos genes para continuar con el desarrollo del nuevo ser. Parece que la sustancia responsable de esto es la cromatina. Hay muchos estudios que indican que este es el mecanismo para que los genes se enciendan o se apaguen para así, generar un tipo de células o dejar de generar algún otro. Sin embargo, no sé de estudios que indiquen por qué, en un momento determinado, se produce este switcheo de genes.

Aquí les hablaré de lo que creo que en realidad pasa. Primero, ¿Por qué la Naturaleza es recursiva? ¿Por qué -si se necesitan más recursos- la Naturaleza impone el modelo recursivo sobre el iterativo? Mi respuesta es que en el modelo recursivo, está encapsulada toda la información que se necesita. En el modelo iterativo requerimos de un valor externo para que la función se ejecute. Y más de uno podría pensar que eso también pasa en las funciones recursivas, y sí, pero solamente para las matemáticas. En el caso de la reproducción no es necesaria información adicional externa para que la recursividad ocurra.

¿Pero por qué no se requiere información externa? Mi respuesta es esta: no hay necesidad de información externa para empezar un proceso, porque éste funciona hasta que ocurre un umbral. Veamos con el ejemplo del árbol lo que quiero decir: Supongamos que armamos un árbol empezando por su tronco. Este, eventualmente disminuye su grosor y nacen ramas, las cuales a su vez, son como pequeños árboles. Si tomásemos fotos de las ramas, pero acercándonos lo suficiente a las mismas, no podríamos saber si se trata de un tronco o de una rama. He aquí la autosimilitud. Y una vez más, esas ramas puedne generar subramas más pequeñas, autosimilares a las primeras, pero a diferente escala. ¿Y cuándo avaba este proceso? Fácil: cuando no hay suficiente material para crear ramas cada vez más pequeñas. Llega un momento que no hay forma que se creen nuevas ramas. Ese es el umbral del que hablo. 

Entonces, para ilustrar la idea. Supongamos que tenemos una función muy compleja, la cual crea un nuevo ser. No requiere de información externa y si requiriese, se puede dar cualquier valor y empezar el proceso, porque la Naturaleza guiará dicho proceso hasta que ocurra un umbral. Más allá de eso no se pueden generar más células, o más ramas, o más subtroncos, etcétera. Y entonces, el switcheo de genes a lo mejor se da cuando se llega al umbral. Con esto podríamos entender la fase temporal del ADN para encender o apagar genes. Es muy sencillo onceptualmente: cuando se llega a un umbral, se detiene ese proceso y probablemente se inicia otro diferente.

Déjenme ilustrarlo con este ejemplo trivial: imaginemos que tenemos mucha hambre y nos sentamos a comer. Podemos comer abundantemente pero de pronto, nuestro cuerpo nos dirá que ya está satisfecho. Ya habremos llegado a un umbral y entonces pasamos a otra fase, a otro proceso, el de hacer la digestión y dejar de seguir comiendo. En este sentido, el sitcheo de comer a no comer no es una cuestión de "timing", sino de llegar a un umbral. Y cuando se llega a éste, se detiene ese proceso y posiblemente se inicia otro.

Estas explicaciones son, en mi opinión, la razón por la cual la Naturaleza prefiere la recursión como mecanismo de repetición de algún proceso. Y es que ese proceso recursivo tiene siempre una condición terminal, que es el umbral, que es la que decide que se cierra el proceso que se está realizando. Por ello, en la recursión todo está encapsulado. Y si hablamos de biología, en la reproducción de los seres vivos, se producen estos umbrales contínuamente hasta que se crea un nuevo ser completo.

¿Opiniones y argumentos? Los leo.

Friday, February 17, 2023

Un descubrimiento personal


En la Facultad de Ciencias doy un curso de Vida Artificial", la cual es una asignatura que trata de simulaciones -a través de la computadora- de manera que se puedan estudiar algunos conceptos de la vida misma, por ejemplo, la reproducción. Para ello se usan los autómatas celulares en una y dos dimensiones (en general), los cuales han sido estudiados ya en los años 70s, 80s y 90s del siglo pasado por Conway (Juego de la vida) y Wolfram (autómatas celulares en una dimensión). De hecho, existe mucha información en la red sobre este tema. Wolfram, por ejemplo, ha escrito un libro enorme, llamado "A New Kind of Science", el cual puede leerse gratuitamente aquí. Por otro lado, el sitio obligado para estudiar el Juego de la Vida de Conway es éste

Pues bien, en el libro de Wolfram, (el cual a pesar de intimidar sólo por su tamaño, es bastante accesible y diría que en general es un libro de divulgación), se habla de los autómatas unidmensionales. Se analizan las 256 reglas. Se hace un análisis de la complejidad de las mismas, etcétera. Wolfram entonces experimenta cambiando reglas y situaciones para la reproducción de los autómatas. Eventualmente demuestra que con ciertas reglas de sustitución, se pueden crear los elementos fundamentales de la máquina de Turing, la cual es una máquina conceptual que puede hacer en principio, cualquier cálculo.

Por ejemplo, en la página 82 de su libro, pone el siguiente gráfico que ilustra un sistema simple de sustitución, el cual genera que se dupliquen los elementos en cada generación. 

Y aunque Wolfram lo muestra con puntos de tonos de gris, su sistema de sustitución es simplemente:

A --> AB

B --> BA

Así, si tenemos una configuración inicial con solamente el elemento A, podeos sustituir y quedarnos lo siguiente:

A --> AB

AB --> ABBA

ABBA --> ABBABAAB

etcétera...

Si contamos los elementos que hay en cada generación, encontraremos que estos se duplican.

La serie de Fibonacci, por ejemplo, puede expresarse como el siguiente esquema de sustitución:

A --> B

B --> BA

Si tenemos la configuración inicial A obtendremos en las siguientes generaciones:

A --> B

B --> BA

BA --> BAB

BAB --> BABBA

BABBA --> BABBABAB

BABBABAB --> BABBABABBABBA

Si contamos los elementos en cada generación, obtendremos la secuencia de Fibonacci: 1, 2, 3, 5. 8. 13, etcétera.

Pues bien, defnir un autómata como un sistema de sustitución generauna gramática y esta es simplemente una serie de símbolos que se manipulan a través de ciertas reglas. Así, aplicando dichas reglas se obtienen las "frases" legales y válidas de acuerdo a la gramática definida.

Los autómatas formales, como los lenguajes, tienen en muchos casos la posibilidad de ser recursivos, se llaman así mismo. Por ejemplo, en el caso de las reglas de sustitución del autómata que duplica los valores de sus elementos en cada generación, podemos ver que A se sustituye por AB y B se sustituye por BA. Hay una suerte de llamada a sí misma en cada regla. Y esto es bastante común en los lenguajes. Por ejemplo, pensemos en la definición recursiva del factorial:

0! = 1 (condición terminal)

n! = n * (n-1)! (recursión)

Por ejemplo, para calcular 3! diremos lo siguiente:

3! = 3 * 2!

necesitamos ahora conocer cuánto es 2!

2! = 2 * 1!

necesitamos ahora conocer cuánto es 1!

1! = 1 * 0!

necesitamos ahora conocer cuánto es 0!, pero eso ya lo sabemos, 0! = 1. 

Entonces empezamos a resolver cada ecuación:

1! = 1 * 0! = 1 * 1 = 1

entonces 2! = 2 * 1! = 2 * 1 = 2

por ende, 3! = 3 * 2! = 3 * 2 = 6.


Otro ejemplo de los lenguajes humanos: Si buscamos la definición de "perro", hallaremos que es "can". Si buscamos la definición de "can", se nos dirá que es "perro". Aquí hay una función recursiva que se cicla, porque no tiene una manera de terminar, de detenerse, como en el caso de la definición de factorial, en donde 0! termina con el cálculo y el ciclado infinito (porque 0! = 1).

Pues bien, se sabe que toda función recursiva se puede poner de forma iterativa. Por ejemplo, el factorial iterativo en Python (no recursivo) es:

def factorial(n):

    result = 1

    for i in range(1, n + 1):

        result *= i

    return result

(gracias a chatgpt)


En cambio, en Python, el factorial recursivo se escribe así:

def factorial(n):

    if n == 0:

        return 1

    else:

        return n * factorial(n-1)

(de nuevo, gracias a chatgpt)

La versión iterativa usa menos recursos de cómputo que la versión recursiva. Entonces, ¿por qué los autómatas celulares e incluso, los seres vivos, usan la recursión para crear otros seres vivos (a su imagen y semejanza)? ¿No debería la Naturaleza usaR la máxima economía en recursos?

Esta es la pregunta que no podía resolver, pero el otro día, caminando por avenida del IMAN, encontré una posible respuesta. De pronto me sentí como el episodio que narra Bertrando Russell cuando se dio cuenta del concepto de la verdad ontológica. Dice Russell que sintió una especie de iluminación, experiencia que pocas veces se da sin la necesidad de sustancias alucinógenas u otros artilugios.

Pues bien, la respuesta a las preguntas que me había planteado sobre los autómatas celulares y a que prácticamente todos usan la recursión, es porque en la misma se encuentra una imagen a escala de lo que está representando. En el caso de la iteracción, necesitamos pasarle los parámetros correctos para que se ejecuten los algoritmos que queremos, pero en la recursión, los datos se incluyen en el mismo algoritmo. Russell, por ejemplo, habla de la paradoja del barbero, en donde se puede expresar en Prolog, donde el paradigma de ese lenguaje hace que las instrucciones y los datos sean indistintos. Véase aquí  para esa discusión.

Así, a mí me queda clarísima la razón por la cual los seres vivos usan funciones recursivas, que se llaman a sí mismas, para la reproducción y creación de los elementos hijos. Y sí, aparentemente ocupa más recursos pero la realidad es que es mucho más eficiente que los algoritmos iterativos.



Tuesday, September 27, 2022

David Heiserman y su libro sobre Inteligencia Artificial

Tengo un libro relativamente antiguo, cuyo autor es el estadounidense David Heiserman que, según entiendo, ha publicado más de 40 obras técnicas y científicas, la mayoría de ellos en la desaparecida editorial TAB Books. El título del libro al que hago mención es "Projects in Machine Intelligence for your Home Computer". La edición que tengo es del año 1982.

El título probablemente llama la atención sobre las pretensiones de la obra. Sin embargo, hoy es no solamente un libro obsoleto, sino bastante malo como para tomarlo con seriedad. Y quiero ser benevolente con el autor. En el momento de la escritura de dicha obra, probablemente el trabajo que hizo se veía razonablemente interesante. Hoy día, no obstante, resulta trivial y primitivo en muchos de los conceptos que expone, aunque desde luego, no podemos sustraerlo de su contexto histórico, es decir, en la época de cuando se escribió el mismo.

Heiserman intenta trabajar con la idea de autómatas celulares, a los cuales pretende buscarles un comportamiento que podamos considerar como complejo, al menos en algún sentido. Para ello, comienza creando una "creatura alpha", la cual es un puntito en la pantalla, encerrado entre cuatro paredes (los límites de la pantalla), y en donde esta creatura se mueve azarosamente uno o dos pasos en todas direcciones. El autor entonces muestra a su creatura moviéndose al azar (aunque jamás discute el tema de que el azar de la computadora no lo es estrictamente) y quiere darle una interpretación a sus movimientos. 

Por ejemplo, decide poner en la pantalla "obstáculos" para ver cómo se mueve dicha creatura cuando enfrenta un nuevo entorno físico Si por ejemplo, le pedimos al programa (del cual Heiserman entrega el código fuente en Applesoft (Apple II) y TRS BASIC (Tandy Radio Shack Z80), que es más de la mitad de las páginas del libro), que la creatura deje un trazo por donde va pasando y éste se vuelva un obstáculo para seguir pasando por ahí (como si creara una pared para la creatura), entonces ésta se comportará de cierta manera. Y explica entonces: "El comportamiento de la creatura mientras trabaja sobre un rastro impenetrable es especialmente significativo en el contexto de la psicología de las máquinas". Una frase que no dice nada, que no explica nada y que quiere convencer que la creatura azarosa hace algo que podríamos considerar inteligente o al menos complejo, que en este contexto sería, difícil de explicar.

Poco después Heiserman hace una nueva creatura a la que llama beta y le otorga un poco de más capacidades, por ejemplo, memoria de eventos pasados para así hacerla "más inteligente", al menos con respecto a la creaura alpha. Pero esta modificación no hace nada significativamente diferente. Simplemente, cuando la creatura beta encuentra un obstáculo y tiene su memoria vacía, actúa como una creatura alpha. Sin embargo, en ese momento -cuando encuentra una salida al problema del obstáculo- guarda esta información en su memoria. Si de pronto encuentra una situación similar, puede acudir a su recuerdo para ver si puede salir del atolladero. 

La realidad es que los experimentos en inteligencia de máquina que propone Heiserman son triviales y además, llenos de interpretaciones dudosas del autor. Déjenme poner esta analogía. Yo tenía una perrita Snauzer, la Pupa, a la cual un día le pedí que buscara su pelota para jugar. La perrita salió en busca del juguete y al rato regresó con una cápsula de huevo kínder (que contiene un juguete), pues no halló su pelota. Yo podría decir: "la Pupa entiende de figuras geométricas y al no encontrar su pelota, decidió traerme lo más parecido a ese objeto". Más de uno podrá decir que esa es meramente una interpretación del evento. ¿Qué tal si simplemente encontró la cápsula y se olvidó de lo que buscaba y por eso te llevó ese objeto? Vamos, no tenemos certezas porque no podemos pedirle a la Pupa que explique porqué actuó así. Entonces queda como una especulación indemostrable. Bueno, pues eso le ocurre a Heiserman con las explicaciones que da en su obra.

Llama la atención que el autor no haya profundizado un poco en la idea planteada en los autómatas en dos dimensiones, como en el caso del "Juego de la Vida" de Conway, que se volvió popular en 1970, por lo que -quiero creer- Heiserman debería haberlo conocido. Y es que los autómatas de Heiserman y sus resultados no llevan a nada. No hay comportamiento complejo ni inteligente. Sólo unos puntos en la pantalla que se mueven de forma azarosa y que en las variaciones que propone el autor, no cambia significativamente su comportamiento.


David Heiserman


El problema con los autómatas de Heiserman es que no ha pensado en el tema de la autoreplicación y la recursión. Vamos, los seres vivos tienen una propiedad increíble: pueden crear copias de sí mismos. Y para ello, se necesita que exista un proceso que permita esta autoreplicación y uno de los procesos que permite un sinfín de ideas complejas y difíciles de explicar es dotar de reglas recursivas a los autómatas. ¿Cómo podemos asegurarnos que las reglas que inventemos sean recursivas? Pues entendiendo que se llaman a sí mismas y que en muchos casos, sino es que todos, estas reglas requieren de un stack para ir guardando valores temporalmente. Desde luego que no es condición sine qua non esto, pero probablemente ayuda.

Reconozco que el libro de Heiserman me decepcionó, pero entiendo que hoy conocemos de temas mucho más sofisticados en los automatas celulares, llámense "sistemas-L", de Lindenmayer, hormigas de Langton o autómatas de Wolfram, entre otros. Quiero creer que la obra de este autor es obsoleta y poco enriquecedora para aquellos que quieren hacer inteligencia artificial con simulaciones de computadora y la culpa es que probablemente Heiserman no tenía acceso a mucho de lo que hoy sabemos del tema..