**IAIC** Soluciones Práctica Semana 5 Búsquedas Ciegas e Informadas # Objetivo Esta práctica comparte mucho de la práctica anterior de representación en Espacios de Estados. En este caso estamos más interesados en las características particulares que debemos añadir para la resolución de los problemas por búsqueda y la implementación de heurísticas específicas para $A^*$. En la medida de lo posible, todas las soluciones deben implementarse haciendo uso de la librería proporcionada en el curso. Además, se proponen algunos ejercicios adicionales para ser realizados en papel y donde poner en práctica conceptos de contenido más teórico sobre las búsquedas estudiadas. # Librerías BFS y A* En la práctica anterior vimos cómo formalizar problemas de representación en Espacios de Estados. En este tema introducimos algunos algoritmos (ciegos e informados) para dar soluciones reales a problemas de búsqueda en esos espacios. !!!Tip
**Detalles: Búsquedas BFS** Lista de ficheros asociados a Búsquedas BFS: + `Uninformed Explorations.nlogo`: Modelo de demostración en el que se muestran diversos recorridos no informados sobre un espacio de estados en forma de árbol. + `BFS.nls`: Fichero Fuente de NetLogo con los algoritmos genéricos asociados al algoritmo BFS. + `LayoutSpace.nls`: Fichero Fuente de NetLogo que contiene la librería para visualizar espacios de estados calculados (para diversas librerías que hacen uso de estructuras similares). + `BFS agents - Template.nlogo`: Modelo de solución BFS del problema del _puzzle numérico_. Esta librería es realmente una extensión de la librería BSS explicada en el tema anterior, en consecuencia, tanto los estados (_AI:states_) como las transiciones (_AI:transitions_) siguen un comportamiento similar, pero se añaden las propiedades: Para los estados: + `path` : Contendrá la lista de estados por los que la búsqueda ha pasado para llegar desde el estado inicial hasta el actual. Las transiciones permanecen igual. La función principal de la librería **BFS** es el procedimiento `BFS`, que construye el grafo de estados que se obtiene a partir de un estado inicial dado (siguiendo los constructores en anchura definidos por el usuario) y comprueba si el estado objetivo ha sido alcanzado o no. `BFS` es por tanto un report, que devolverá: + `False` si no ha habido éxito en la búsqueda. + El *estado final* alcanzado, si dicho estado es un estado final válido que resuelve el problema. Ha de tenerse en cuenta que la búsqueda podría dar como resultado un proceso infinito, lo que en NetLogo puede provocar problemas de estabilidad en el motor de ejecución. Los datos de entrada de `BFS` son: + `#initial-state` : Contenido del estado inicial que da comienzo a la búsqueda. + `#final-state` : Contenido del estado final que se busca. En caso de que el estado final venga dado por una condición de parada, pero no por un estado concreto (como en el problema de las _N reinas_), esa condición de parada vendrá dada por un report que se explicará a continuación, y en este caso podemos pasarle cualquier dato como estado final, ya que no lo considerará. + `#debug?` : `True / False` - Indica si se mostrará el contenido en los estados, y las reglas en las transiciones. + `#visible?` : Muestra/Oculta estados y transiciones en el interfaz. Para el correcto funcionamiento de esta librería en el modelo principal se deben definir las siguientes funciones: + `AI:children-states` : Idéntico al caso `BSS`. + `AI:final-state?` : Función booleana de estados. Devuelve si el estado actual debe ser considerado un estado final. Este procedimeinto recibe como entrada un parámetro que, por ejemplo, permita comparar el estado actual con el `#final-state` en caso de que dicho estado final sea un estado concreto. Si no fuera así, el parámetro pasado no tiene utilidad y la verificación de si es final o no solo se basará en propiedades internas del estado actual. + `AI:equal?` : report que decide cuándo dos estados son iguales. Muchas veces podrá ser simplemente el comparador `=`, pero en caso de que el contenido de los estados sea una estructura más compleja (por ejemplo, un conjunto representado por una lista) puede ser necesario definir una igualdad más elaborada (por ejemplo, en el caso anterior, que no dependa del orden). Adicionalmente, debido a que `BFS` devuelve el estado final completo, del que podemos extraer el `path` que ha permitido construirlo, la librería proporciona un _report_ auxiliar, `extract-transitions-from-path`, que al ser ejecutado por un estado devuelve, a partir de la información almacenada en el estado, la sucesión (lista) de transiciones que se han aplicado para llegar desde el estado inicial hasta él. Obsérvese que este procedimiento no necesita ningún dato de entrada, ya que lo ejecuta el propio estado y él puede acceder a toda su información de manera directa. ~~~~ to-report extract-transitions-from-path report (map [[x1 x2] -> AI:transition ([who] of x1) ([who] of x2)] (bl path) (bf path)) end ~~~~
!!!Tip
**Detalles: Búsqueda $A^*$ ** Lista de ficheros asociados a Búsquedas $A^*$: + `A-star.nls`: Fichero Fuente de NetLogo con los algoritmos genéricos asociados a la Búsqueda A*. + `LayoutSpace.nls`: Fichero Fuente de NetLogo que contiene la librería para visualizar espacios de estados calculados (para BFS y otras librerías que hacen uso de estructuras similares). + `A-star Problem Solver - Template.nlogo`: Modelo de solución A* para usar como plantilla en la práctica. Esta librería es realmente una extensión de la librería BFS, en consecuencia, tanto los estados (_AI:states_) como las transiciones (_AI:transitions_) siguen un comportamiento similar, pero se añaden las propiedades: Los estados permanecen igual. Para las transiciones: + `rule` : Al igual que en los casos anteriores, las reglas se representan como una lista `["rep" coste ..]`, donde destaca la segunda componente con el coste de aplicar dicha regla. + `cost-link` : Almacena el coste de la transición. Además, el algoritmo A* almacena toda la información de la búsqueda en una familia adicional de agentes denominada AI:searchers, que tienen las siguientes propiedades: + `memory` : Almacena el camino de nodos que ha recorrido A* desde el estado inicial hasta el nodo en el que se encuentra este buscador. + `cost` : Almacena el coste real del camino recorrido desde el estado inicial. + `total-expected-cost` : Almacena el coste total esperado desde el estado inicial hasta el objetivo. + `current-state`: Almacena el estado (_AI:state_) en el que se encuentra este buscador. + `active?` : Establece si este buscador esta activo, es decir, si los estados vecinos al suyo han sido explorados o no. La función principal de la librería **A-star** es el procedimiento `A*`, que construye el grafo de estados que se obtiene a partir del estado inicial (siguiendo la selección heurística definida por el usuario) y comprueba si el estado objetivo ha sido alcanzado o no. El grafo de estados subyacente se va construyendo de forma dinámica a medida que va siendo necesario. `A*` es por tanto un _report_, que devolverá: + `False` si no ha habido éxito en la búsqueda. + La memoria del buscador que ha alcanzado un estado final. Ha de tenerse en cuenta que la búsqueda podría dar como resultado un proceso infinito, lo que en NetLogo puede provocar problemas de estabilidad en el motor de ejecución. Los datos de entrada que espera `A-star` son los mismos que `BFS`: `#initial-state`, `#final-state`, `#debug?`, `#visible?`. Para el correcto funcionamiento de esta librería en el modelo principal se deben definir las siguientes funciones: + `AI:children-states` : Función de estados que devuelve una lista con información sobre los posibles sucesores del estado que lo ejecuta. Cada estado devuelto deber ser un par `[s r]`, donde `s` es el contenido del nuevo estado, y `r` es una regla con la estructura esperada (`["rep" c ...]`). + `AI:final-state?` : Similar al de `BFS`. + `AI:heuristic` : Función de buscadores que devuelve el valor de la heurística que evalúa la distancia entre su localización/estado y el objetivo. + `AI:equal?` : report que decide cuándo dos estados son iguales. Muchas veces podrá ser simplemente el comparador `=`, pero en caso de que el contenido de los estados sea una estructura más compleja (por ejemplo, un conjunto representado por una lista) puede ser necesario definir una igualdad más elaborada (por ejemplo, en el caso anterior, que no dependa del orden).
!!!warn: Descargas 1. [Aquí puedes descargar un ZIP con los ficheros necesarios para realizar esta práctica](P5.zip). 2. Las plantillas añaden algunas características al modelo que facilitan la representación visual del Espacio de Estados generado. 2. Recuerda que es conveniente ir haciendo copias/renombramiento de la Plantilla para cada uno de los ejercicios que vayas a resolver. # Problemas **Problema 1**.- Aplica **BFS** a los problemas vistos en la práctica anterior. !!!Alg:
**Solución** Debido a que no hay una gran diferencia en la representación del problema para generar un espacio de estados y aplicar BFS sobre el espacio generado, solo vamos a mostrar un ejemplo de uso sobre uno de los problemas vistos en la práctica anterior: **el problema de las 2 jarras**. La solución concreta la puedes encontrar en la práctica anterior, pero recordemos que dábamos una definición explícita de las transiciones aplicables: ~~~~~~~~~~~~~~ to-report Tr report (list (list "Empty(1)" ([ s -> (list 0 (last s)) ])) (list "Empty(2)" ([ s -> (list (first s) 0) ])) (list "Pour 1 to 2" ([ s -> pour1-2 (first s) (last s) ])) (list "Pour 2 to 1" ([ s -> pour2-1 (first s) (last s) ])) (list "Fill(1)" ([ s -> (list 3 (last s)) ])) (list "Fill(2)" ([ s -> (list (first s) 4) ])) ) end to-report pour1-2 [x1 x2] ... end to-report pour2-1 [x1 x2] ... end to-report valid? [x] let J1 first x let J2 last x report ((J1 <= 3) and (J2 <= 4) and (J1 >= 0) and (J2 >= 0)) end to-report AI:children-states report filter [ s -> valid? (first s) ] (map [ t -> (list (run-result (last t) content) t) ] Tr) end ~~~~~~~~~~~~~~ Solo tenemos que añadirle los report asociados a reconocer si un estado es final, y el de igualdad. En este caso, ambos son absolutamente estándar y reutilizables para muchísimos casos: ~~~~~~~~~~~~~~ to-report AI:final-state? [params] report ( content = params) end to-report AI:equal? [a b] report a = b end ~~~~~~~~~~~~~~ Podemos usar este caso para poner un ejemplo de cómo definir sencillamente un evaluador de estados finales que no sea llegar exactamente a un estado concreto. Por ejemplo, si se pidiera como solución al problema llegar a tener $2$ litros de agua en cualquiera de las jarras, podríamos definir: ~~~~~~~~~~~~~~ to-report AI:final-state? [params] report member? 2 content end ~~~~~~~~~~~~~~ Adicionalmente, puede ser interesante ver cómo podemos usar la función `BFS` proporcionada por la librería y recuperar el camino encontrado desde el estado inicial hasta un objetivo (se supone que tanto `Initial_State` como `Final_State` son los contenidos inicial y final, respectivamente, de las jarras, por ejemplo `[0 0]` y `[0 2]`): ~~~~~~~~~~~~~~ to test ca let p BFS Initial_State Final_State True True if p != false [ ask p [ print "The solution is: " foreach (map [ t -> [first rule] of t ] extract-transitions-from-path) print ] ] end ~~~~~~~~~~~~~~
!!!Warn: Cosas a tener en cuenta... 1. La única diferencia entre modelar un problema para `BSS` y `BFS`/`DFS` es que en estos últimos casos hemos de proporcionar una función que verifique si hemos alcanzado un estado final o no. 2. Si el algoritmo encuentra solución, el resultado que devuelve la función `BFS` es un `path` (camino que lleva del estado inicial a la solución encontrada). En caso contrario, devolverá `False`. **Problema 2**.- Busca heurísticas para resolver los problemas vistos en la práctica anterior por medio de $A^*$. !!!Warn: Cosas a tener en cuenta... 1. Puedes encontrar muchos problemas para los que no es natural definir una heurística para ser resuelto por $A^*$. 2. En general, la búsqueda de una heurística adecuada es un problema complicado y no automatizable. **Problema 3**.- **El problema del 8-puzzle**: es un puzzle de deslizamiento que consiste en un conjunto de $8$ piezas en un marco de tamaño $3\times 3$ (por lo que queda un hueco libre). El objetivo del puzzle consiste en encontrar la sucesión de movimientos (moviendo piezas al hueco libre) que llevan la distribución inicial a una distribución ordenada prefijada. Se pueden pensar variantes de distinto tamaños, por ejemplo, el $15$-puzzle, que usa un tablero de tamaño $4\times 4$. Plantea un coste para las operaciones válidas y analiza las siguientes heurísticas cuando se hace uso de $A^*$: * $H_1$: número total de fichas descolocadas. * $H_2$: Suma de distancias de Manhattan (entre la posición real de cada pieza y su posición final). * $H_3= H_1+2\cdot H_2$. !!!Alg:
**Solución** Una de las formas más directas de representar un estado en este problema es usar una matriz que sea una ordenación al azar de: $$ \left[{\begin{array}{ccc} 0 & 1 & 2\\ 3 & 4 & 5\\ 6 & 7 & 8 \end{array} }\right] $$ Como, por ejemplo: $$ \left[{\begin{array}{ccc} 8 & 1 & 4\\ 7 & 5 & 3\\ 6 & 0 & 2 \end{array} }\right] $$ donde $0$ representa el hueco. Nosotros usaremos una representación en forma de lista, por lo que los estados anteriores serían en nuestro espacio: $$[0\ 1\ 2\ 3\ 4\ 5\ 6\ 7\ 8]$$ $$[8\ 1\ 4\ 7\ 5\ 3\ 6\ 0\ 2]$$ En el caso de querer resolver el problema con $A^*$ las reglas tendrán la forma `[ rep cost f ]`, donde: * `rep` es una representación de la transición/acción/movimiento. * `cost` es el coste de aplicar esa transición a un estado. * `f` contiene la información necesaria para aplicar la transición de forma efectiva. Para una posición dada de la lista que representa el estado, `h`, `(movements h)` devuelve la lista de posibles movimientos/intercambios con la ficha/hueco de la posición `h` (por ejemplo, la posición $0$ puede intercambiarse con $1$ y $3$). ~~~~~~~~~~ to-report movements [h] let movs [[1 3] [0 2 4] [1 5] [0 4 6] [1 3 5 7] [2 4 8] [3 7] [6 4 8] [5 7]] report item h movs end ~~~~~~~~~~ Para un estado dado, `s`, `(swap i j s)` devuelve un nuevo estado en el que las fichas de las posiciones `i` y `j` han sido intercambiadas: ~~~~~~~~~~ to-report swap [i j s] let old-i item i s let old-j item j s let s1 replace-item i s old-j let s2 replace-item j s1 old-i report s2 end ~~~~~~~~~~ El procedimiento `AI:children-states` devuelve los sucesores del estado actual: una lista de pares `[ns tran]`, donde `ns` es el contenido del nuevo estado, y `tran` es la transición aplicable para obtenerlo (en este caso, todas ellas con igual coste, $1$). Este report busca el hueco (posición del $0$), calcula los movimientos posibles desde esa posición (con `movements`), y los aplica (con `swap`): ~~~~~~~~~~ to-report AI:children-states let i (position 0 content) let indexes (movements i) report (map [ j -> (list (swap i j content) (list (word "T-" j) 1 "regla")) ] indexes) end ~~~~~~~~~~ El estado final es un estado concreto, por lo que podemos usar la misma función de detección de los algoritmos anteriores, y lo mismo con el report que decide la igualdad de estados: ~~~~~~~~~~ to-report AI:final-state? [params] report ( content = params) end to-report AI:equal? [a b] report a = b end ~~~~~~~~~~ Y ahora necesitamos definir la heurística que usarán los buscadores durante la selección del siguiente estado. Observa que la heurística depende del estado objetivo (que esperan como dato de entrada con `#Goal`). La Heurística $H_1$ podría ser: ~~~~~~~~~~ to-report H1 [#Goal] report length filter [ x -> x = False] (map = c #Goal) end ~~~~~~~~~~ Las Heurísticas $H_2$ y $H_3$ podrían ser: ~~~~~~~~~~ to-report H2 [#Goal] let pos [[0 0] [0 1] [0 2] [1 0] [1 1] [1 2] [2 0] [2 1] [2 2]] let c [content] of current-state report sum (map [ j -> mdist (item (position j c) pos) (item (position j #Goal) pos) ] (range 9)) end to-report H3 [#Goal] report (H1 #Goal) + 2 * (H2 #Goal) end ~~~~~~~~~~ Finalmente, usamos la heurística que queramos: ~~~~~~~~~~ to-report AI:heuristic [#Goal] report H1 #Goal end ~~~~~~~~~~ Recordemos que la **Distancia de Manhattan** se puede calcular definiendo adecuadamente el procedimiento: ~~~~~~~~~~ to-report mdist [x y] report sum map abs (map - x y) end ~~~~~~~~~~ En el fichero `"8 puzzle A-star.nlogo"` del ZIP anterior puedes encontrar un uso de esta solución con una implementación visual del juego. Es un ejemplo de cómo se pueden usar las soluciones proporcionadas por un buscador para crear soluciones visuales.
**Problema 4**.- **$A^*$ para Mallas Cuadradas y Grafos Geométricos**: Comprueba la acción de distintas distancias (heurísticas) para encontrar caminos mínimos en mallas cuadradas y en grafos geométricos (un grafo geométrico $G(n,r)$ consta de $n$ puntos al azar en $\mathbb{R}^2$, y donde se conectan los puntos que tengan una distancia menor a $r$). !!!Alg:
**Solución** Utiliza `"A-star_patches_tie.nlogo"` y `"A-star Turtles Geometric Network LT.nlogo"` para experimentar con las ideas que se proponen en el ejercicio.
!!!Warn: Cosas a tener en cuenta... 1. La implemetación de `"A-star Turtles Geometric Network LT.nlogo"` hace uso de una librería alternativa `"A-star-LT.nls"`, que es una adaptación de la librería que usa agentes, pero usando Tablas como estructura de datos para almacenar los estados. El interfaz de uso en ambos casos es similar, pero la que hace uso de tablas es considerablemente más rápida y consume menos memoria. 2. Ten en cuenta que en el ejemplo que trabaja sobre patches no se usa la implementación de $A^*$ proporcionado en la librería, sino que se ha definido en el propio modelo para poder modificarlo adaptándolo a las diversas variaciones que se consideran. **Problema 5**.- El algoritmo $A^*$ no especifica qué ocurre cuando hay varios nodos en la frontera que tienen el mismo valor de $f$. Compara las siguientes técnicas de desempate e intenta conjeturar primero cuál crees que funcionará mejor sobre algunos ejemplos. Pruébalo sobre ejemplos en los que haya varios caminos óptimos hasta el objetivo (por ejemplo, en mallas cuadradas con la distancia euclídea o de Manhattan como heurísticas, porque se producen muchos empates): * aleatoriamente con distribución uniforme, * el nodo que lleva en la frontera más tiempo, * el nodo que lleva en la frontera menos tiempo, * el nodo con menor valor de $h$, * el nodo con menor coste. (Las dos últimas necesitan alguna otra técnica de desempate si $h$/coste son iguales). **Problema 6**.- El grafo de la figura siguiente muestra el espacio de estados de un hipotético problema de búsqueda. Los estados se indican con letras, y el coste de cada acción se indica en la arista correspondiente. Obsérvese que las acciones no son reversibles, ya que el grafo es dirigido. La tabla que aparece junto al espacio de estados muestra el valor de una función heurística admisible, considerando $G$ como objetivo (comprueba que esta heurística nunca sobreestima el mínimo coste del camino desde cualquier estado hasta $G$). ![](../img/astar1.jpg) Considerando $S$ como estado inicial, resuelve el problema de búsqueda anterior utilizando: * BFS. * DFS. * $A^*$ con la heurística anterior. Al dar el árbol de búsqueda debe indicarse: - el orden de expansión de cada nodo (por ejemplo, numerando los nodos expandidos según el orden de su expansión), - la acción correspondiente a cada arista del árbol, - el estado, - el coste del camino, y - el valor de la heurística de cada nodo. **Problema 7**. ![](../img/astar2.jpg align="right" width=300px)Para cada una de las siguientes estrategias de búsqueda para el Espacio de Estados representado por el grafo adjunto, calcula el orden en el que se expanden los estados y el camino devuelto por la búsqueda. En todos los casos, los empates se resuelven de manera que los estados con un orden alfabético anterior se expanden primero. El estado inicial y el estado objetivo son $S$ y $G$, respectivamente. * BFS. * DFS. * Búsqueda $A^*$ con la heurística $h$. **Problema 8**. En el espacio de estados que se muestra con el grafo siguiente: ![](../img/astar3.jpg width=700px) Considerando que $A$ es el estado inicial y $G$ es el objetivo. Los costes de cada arista (que pueden ser atravesadas en ambas direcciones) se muestran en el grafo. * Demuestra que la heurística $h_1$ es consistente pero la heurística $h_2$ no lo es. * Para cada uno de los algoritmos de la siguiente tabla, marca cuál (si lo hay) de los caminos señalados podría devolver. Ten en cuenta que para algunas estrategias de búsqueda el camino específico devuelto podría depender del comportamiento de desempate. En tales casos, asegúrate de marcar todos los caminos que podrían ser devueltos bajo algún esquema de desempate. |Algoritmo | A-B-D-G | A-C-D-G | A-B-C-D-F-G| |:--------:|---------|---------|------------| |BFS | | | | |DFS | | | | |$A^*(h_1)$| | | | |$A^*(h_2)$| | | | * Supongamos que estamos completando la nueva función heurística $h_3$ que se muestra a continuación, donde todos los valores son fijos excepto $h_3(B)$. |Nodo | A | B | C | D | E | F | G | |:---:|---|---|---|---|---|---|---| |$h_3$|$10$| ? |$9$|$7$|$1.5$|$4.5$|$0$| Para cada una de las siguientes condiciones, escribe el conjunto de valores posibles para $h_3(B)$: 1. ¿Qué valores de $h_3(B)$ hacen que $h_3$ sea admisible? 2. ¿Qué valores de $h_3(B)$ hacen que $h_3$ sea consistente? 3. ¿Qué valores de $h_3(B)$ harán que la búsqueda $A^*$ del grafo expanda el nodo $A$, luego el nodo $C$, luego el nodo $B$, y luego el nodo $D$ en ese orden? **Problema 9**. $A^*$ es un algoritmo que se usa profusamente en los sistemas de navegación (por ejemplo, para ayudar a la conducción de vehículos), pero habitualmente la búsqueda se orienta por medio de una heurística de mínima distancia, cuando no siempre es lo deseable. Por ejemplo: promocionar rutas turísticas/paisajísticas, facilitar trayectos en los que se producen giros más sencillos, compensar los tiempos de tráfico denso en determinadas zonas, sistemas de transporte multimodal, etc. Idea y diseña (haciendo uso de las posibilidades que tiene NetLogo desde el punto de vista gráfico y de agentes) situaciones en las que tenga sentido el uso de heurísticas distintas (o de combinación de heurísticas) en sistemas de recomendación de rutas o en sistemas de navegación automática. # Práctica Entregable Las condiciones para la entrega son: 1. **Utiliza la plantilla** proporcionada en la práctica para escribir la solución al problema propuesto. 2. **Comprime la carpeta completa** (incluyendo los ficheros de las librerías proporcionadas) en un fichero ZIP. 3. **Pon tu nombre al fichero**, con el siguiente formato: `Apellido1_Apellido2_Nombre.Zip` 4. **Envíalo por correo** a la dirección que el profesor anotará en la pizarra. 5. **Espera a la confirmación** de recepción por parte del profesor antes de apagar el ordenador. !!!Alg
**Enunciado** Define los elementos necesarios para resolver el problema de ordenación de listas numéricas aplicando el algoritmo $A^*$ utilizando como transiciones válidas las transposiciones (la *transposición* $i$ en una lista $L$ intercambia los elementos que ocupan las posiciones $i$ e $i+1$ de $L$). Puedes suponer que todas las transposiciones tiene un coste uniforme de valor $1$.