Colaboradores

jueves, 13 de enero de 2011

Algoritmos genéticos

Un poco de biología.

Cada individuo de cada una de las especias que habitan en nuestro planeta poseen ciertas características que lo identifican. Si hablamos de seres humanos, cada uno posee cierta estatura, cierto color de ojos y de cabello y cierto tipo sanguíneo, entre otras muchas. Estas características "externas", aunque algunas de ellas no se puedan ver, como el tipo sanguíneo, constituyen lo que se denomina el fenotipo de un individuo. Cada una de estas características es igual a la correspondiente de alguno de los antecesores del individuo, es decir, nos son dadas por herencia, o por lo menos nos es dada cierta predisposición a ella (como la diabetes, por ejemplo). El fenotipo es resultado de la interacción del medio ambiente en que se desarrolla un individuo y la herencia que éste recibe de sus ancestros. La herencia impone ciertos límites o predisposiciones que, al sumarse con el medio, generan el fenotipo. A veces el medio no importa, por ejemplo, no puede intervenir en nuestro color de ojos (hasta donde mi conocimiento sabe), pero en otras influye de manera determinante. Si se posee cierta predisposición a padecer enfermedades cardiovasculares pero se tiene una excelente condición aeróbica desde pequeño, posiblemente éstas nunca se padezcan. El fenotipo de cada individuo está determinado por las proteínas que produce, y esto a su vez está definido en la información genética de cada una de sus células.

La información acerca de cuáles proteínas se producirán está contenida en los cromosomas del individuo. En cada célula somática (aquellas que constituyen el organismo) existen dos juegos de cromosomas que definen las mismas características; un juego es aportado del padre del individuo y el otro lo es de la madre. Un ser humano posee 23 pares de cromosomas.

Hay células especiales llamadas gametos, que intervienen en la reproducción (los espermatozoides y los óvulos humanos pertenecen a esta categoría). Los gametos no se reproducen por mitosis como el resto de las células, el proceso de división se llama en este caso meiosis. En la mitosis las ćelulas producidas son diploides, mientras que en la meiosis el resultado, los gametos, son haploides, sólo tienen un juego de cromosomas.

Partiendo de una célula diploide el proceso meiótico se realiza de la siguiente manera:
  1. Se duplica el número de cromosomas en la célula, esto es, se hace una copia de cada cromosoma. Al final quedan dos juegos correspondientes al padre y dos a la madre.
  2. Se cruzan un juego de cromosomas del padre con uno de la madre, formándose dos juegos de cromosomas híbridos. El resultado es un juego de cromosomas puros del padre, un juego puro de la madre y dos juegos de cromosomas híbridos.
  3. Se divide la célula dos veces y al final del proceso quedan cuatro células haploides: una con cromosomas puros del padre, una con cromosomas puros de la madre y dos con cromosomas híbridos.
Algoritmos genéticos.

En la naturaleza las características de los seres vivos, incluso aquéllas que los hacen óptimos para habitar en su medio, están determinadas por las proteínas que producen. A su vez, como hemos visto, estas proteínas (o más bien, los aminoácidos que las forman) se codifican en el material genético contenido en cada una de las células del individuo. Así pues, la naturaleza ha mapeado cada posible solución al problema de crear un individuo óptimo en una secuencia particular de bases que producirá ciertas proteínas, ha codificado el dominio del problema (todos los posibles individuos) mapeándolo al conjunto de todas las posibles secuencias de nucleótidos.

Así, para un algoritmo genético lo primero que se requiere es determinar en qué espacio se encuentran las posibles soluciones al problema que se pretende resolver. En caso de tener un problema de optimización de una función cuyo dominio es un subconjunto de los números reales, entonces este subconjunto es al que nos referimos. Pero el algoritmo opera sobre "códigos genéticos", sobre genotipos que se deberán mapear al espacio de soluciones. Es decir, es necesario codificar de alguna manera el dominio del problema para obtener estructuras manejables que puedan ser manipuladas por el AG (algoritmo genético). Cada una de estas estructuras constituye el equivalente al genotipo de un individuo en términos biológicos. El elemento del dominio del problema al que se mapea este genotipo es el análogo al fenotipo. Es frecuente que el código de los elementos del dominio del problema utilice un alfabeto binario (0 o 1).

Una vez que se ha definido la manera de codificar los elementos del dominio del problema y se conoce la forma de pasar de un elemento a su código y viceversa, es necesario fijar un punto de partida. Los algoritmos genéticos manipulan conjuntos de códigos en generaciones sucesivas. Nuevamente haciendo una analogía, manipulan poblaciones de códigos. En éstas un código puede aparecer más de una vez. El algoritmo se encargará de favorecer la aparición en la población de códigos que correspondan a elementos del dominio que estén próximos a resolver el problema. En resumen, el algoritmo recibirá como entrada una población de códigos y a partir de ésta generará nuevas poblaciones, donde algunos códigos desaparecerán mientras que otros, que se mapean en mejores soluciones posibles, aparecen con más frecuencia hasta que se encuentra una satisfactoria o hasta que se cumple alguna otra condición de terminación. Para establecer una base nemotécnica, los códigos en una población, es decir, los elementos de ésta serán llamados individuos y a los códigos en general, ya no en el contexto exclusivo de una población, se les denominará indistintamente cromosomas, genotipo, genoma o código genético, por analogía con los términos biológicos de donde surgen.

Computación evolutiva

¡Hola a todos!, esta es mi primera entrada en el blog de este año (ya se que es muy tarde pero no había tenido tiempo para escribir esta entrada), les traigo aquí unas notas que tomé de mi curso de computación evolutiva espero sean de su agrado.

La computación evolutiva constituye un conjunto de técnicas heurísticas utilizadas para resolver problemas de diversos tipos, en los que podemos encontrar comúnmente problemas de búsqueda y optimización. La característica principal de estos problemas es la gran cantidad de variables que pueden estar involucradas junto con un enorme espacio de posibles soluciones, a veces tan grande como el número de átomos que constituyen el universo.

Los mecanismos de estos algoritmos se inspiran en el proceso de la evolución biológica, se retoman conceptos como genotipo y fenotipo, los cuales son los principales componentes de la unidad fundamental "el individuo", y como motor de variabilidad se identifican procesos genéticos como la reproducción y la mutación, así como la adaptabilidad del individuo en su entorno.

Estas técnicas se han aplicado en distintas disciplinas y ramas del conocimiento desde aplicaciones en inteligencia artificial hasta problemas en ingeniería, debido a que es posible encontrar soluciones relativamente buenas en un periodo corto de tiempo.

Aquí les ofrezco una pequeña introducción a la computación evolutiva, donde les mostraré los fundamentos, teoría y aplicaciones de las principales técnicas evolutivas como son: los agoritmos genéticos, estrategias evolutivas, programación evolutiva y programación genética.

Python: trabajando con un entorno gráfico (wxPython)

Anteriormente les hablaba de python y sus ventajas, hoy les mostraré como hacer de manera sencilla una aplicación gráfica con python, solo que en esta ocasión nos le hablaré de la interfaz nativa de python que es el módulo Tkinter que permite realizar interfaces gráficas basadas en la biblioteca Tk, sino de otro binding cuyo nombre es wxPython, que proviene de la biblioteca gráfica wxWidgets; esta elección es porque Tkinter es demasiado básico, además de no utilizar bindings nativos del sistema, cosa que wxPython si hace.
WxPython trabaja muy bien en diversas plataformas generando el aspecto nativo de las GUI realizadas con él. El programa es el siguiente:

#! /usr/bin/python
import  wx, os 
#modulos para trabajar con el entorno 
#grafico y el sistema operativo
from wx.lib.wordwrap import wordwrap

#El mensaje de la licencia de nuestra aplicacion
licenceText = "Este es una muestra del poder de python XD"

#Tipo de archivos de ejemplo, estos son utilizados para
#filtrar por extension al momento de invocar "abrir un archivo"
wildcard = "Archivos de audio mp3 (*.mp3)|*.mp3|" \
     "Documento de Word (*.doc)|*.doc|"\
     "Todos los archivos(*.*)|*.*"


#Clase de nuestra ventana de ejemplo     
class MyFrame(wx.Frame):
 
 def __init__(self):
  """Esta tambien es otra forma de crear comentarios 
  utilizando varias lineas, equivaldria a /** ... */ en Java o C.
  Este es el metodo constructor que permite inicializar a nuestra 
  ventana, es el equivalente al constructor de Java"""
  
  #Llamamos a la clase Frame que construira nuestra ventana
  wx.Frame.__init__(self,None,-1,"Mi Ventana",size=(400,400))
  
  #El panel de nuestra ventana para poner botones y demas artilugios
  panel = wx.Panel(self,-1)
  
  #Un boton que mostrara un dialogo "Sobre..."
  b = wx.Button(panel,-1,"Hola Mundo!",(20,10))
  
  #integramaos el boton en la ventana
  self.Bind(wx.EVT_BUTTON,self.OnButton,b)
  
  #Otro boton que abrira un dialogo de abrir un archivo
  b = wx.Button(panel,-1,"Abrir algun archivo",(20,50))
  
  #igualmente integramos este boton al al panel
  self.Bind(wx.EVT_BUTTON,self.OnButton2,b)
  
 def OnButton(self,evt):
  """Metodo que reaccionara cuando presionemos el boton Hola Mundo!"""
  
  #Creamos el dialogo
  info = wx.AboutDialogInfo()
  
  #le ponemos nombre superior de nuestro dialogo
  info.Name = "Mi Ventana"
  
  #le asignamos la version de nuestra aplicacion
  info.Version = "1.0"
  
  #Un copyright
  info.Copyright = "(C) Copyright Miguel Pinia"
  
  #Una descripcion de nuestra aplicacion
  info.Description = wordwrap("Este es la informacion sobre la aplicacion: "
  "esta aplicacion fue hecha en"
  " python ", 350, wx.ClientDC(self))
  
  #Asignamos la licencia que creamos al principio del documento
  info.License = wordwrap(licenceText,500,wx.ClientDC(self))
  
  #Desarrolladores de la aplicacion
  #Nota: Vypi es una mascota XD
  info.Developers = ["Miguel Pinia","wxPython","Vypi la mascota"]
  
  #la que contendra nuestra informacion
  wx.AboutBox(info)
 def OnButton2(self,evt):
  """Este metodo reaccionara al presionar el boton "abrir" generando 
  un evento que permitira crear un dialogo de apertura a nuestro archivo, 
  claramente este archivo seleccionado no hara nada, ya que no vamos a trabajar
  sobre el, dado que esta es una aplicacion de ejemplo"""
  
  #Creamos el dialogo y lo mostramos
  dlg = wx.FileDialog(self,message="Escoga un archivo",defaultDir = os.getcwd(),defaultFile="",wildcard=wildcard ,style= wx.OPEN | wx.MULTIPLE | wx.CHANGE_DIR)
  
  #Si ya seleccionamos el archivo lo unico que haremos sera destruir el dialogo
  if dlg.ShowModal() == wx.ID_OK:
   dlg.Destroy
   
   
if __name__=="__main__":
 """Metodo main"""
 app = wx.App()
 
 #creamos un objeto de la clase myframe
 frame = MyFrame()
 
 #Lo mostramos hasta que algun evento ocurra
 frame.Show(True)
 app.MainLoop()

Con esto hemos generado una pequeña aplicación gráfica de manera sencilla, de hecho más rápido y sencillo que otras alternativas en otros lenguajes de programación como Java o C (de hecho, es más sencillo que programar en Swing con Java o GTK+ con C :P). Trabajar con python puede ser más sencillo y enriquecedor, si lo que queremos es aprender a programar. WxPython es muy sencillo de utilizar y muy bonito ver que funciona en las distintas plataformas que existen, para ilustrarlo pondre unas capturas de como trabaja en Windows XP y Ubuntu.


Windows.

















































Ubuntu:



Aquí es mostrada la apariencia nativa del mismo programa en Ubuntu, aqui se ve claramente que no hubo necesidad de cambiar el código original.
Antes de terminar, les pondré unos cuantos enlaces para mayor información:

Con esto he terminado esta pequeña reseña sobre como trabajar gráficos en python.

miércoles, 12 de enero de 2011

Python

Hoy les hablaré de Python como lenguaje de programación y sus características, así como un pequeño programa realizado en él. Python es un lenguaje de programación de alto nivel y está orientado a tener una filosofía que hace hincapié en tener una sintaxis muy limpia y legible.
Es un lenguaje que puede ser considerado multiparadigma, ya que soporta programación orientada a objetos, programación imperativa y en menor medida programación funcional.Python fue diseñado para ser leído con facilidad. Entre otras cosas se utilizan palabras en inglés donde otros lenguajes utilizarían símbolos (por ejemplo, los operadores lógicos !, || y && que son de uso común en Java, en Python se escriben not, or y and, respectivamente).
Para ilustrar un poco la forma de uso de Python, escribiré un pequeño programa:
#! /usr/bin/python 

#La línea anterior sirve para indicarle
#donde se encuentra el intérprete en nuestro sistema
#mientras que estas son solo comentarios de nuestro programa

nombre = raw_input("Dame tu nombre\n")

print nombre, "¡Has escrito tu primer programa en Python!"

Guardamos nuestro programa en un documento con terminación .py, por ejemplo "miPrimerPrograma.py" y ejecutamos en la terminal de la siguiente manera:

python miPrimerPrograma.py

y mostrará en pantalla lo siguiente:
Dame tu nombre
Miguel
Miguel ¡Has escrito tu primer programa en Python!

Así de sencillo hemos hecho un primer programa en Python.
Ahora les explicare que hace cada una de las líneas; en la primera linea se indica la ubicación del intérprete, esto iniciando con "#!", que indica la ubicación. La líneas subsecuentes que inician con el símbolo "#" a solas, sirven para escribir comentarios sobre nuestro programa. Siguiendo con este orden, después tenemos a la línea que inicia con la variable "nombre", en  esta guardaremos lo que leamos de la entrada estándar, esta invocación para la lectura de teclado la hacemos con la función "raw_input()", para que así lo que escribamos se lo asignemos a la variable "nombre". Terminando con esto, imprimimos en la salida estándar lo que leímos junto con un mensaje de aliento por el trabajo que hemos hecho hasta ahora.
Con el programa anterior se muestra lo sencillo que es empezar a programar en python.

martes, 11 de enero de 2011

El proceso del software (finalizando)

Continuando con la breve explicación de lo que es el proceso del software, en este post terminaré con los otros pasos que conllevan el proceso.
Anteriormente había comentado sobre la especificación del problema y el análisis y el diseño del algoritmo, ahora, para terminar les hablaré sobre  los siguientes pasos:

  • Implementación o construcción del modelo:
    • En éste paso o etapa, debemos de pasar nuestro algoritmo a un lenguaje de programación que hayamos elegido. A esta etapa se le conoce codificación. Esta etapa no debería de resultar difícil si es que hemos realizado un diseño que siga la filosofía del lenguaje.
  • Validación:
    • En esta etapa lo que se hace es probar lo que nuestro programa debe de hacer en principio para lo que fue diseñado. Para esto se realizan pruebas informales, que consisten en presentarle al programa diversos conjuntos de datos y verificar que el programa hace lo que tiene que hacer. En este tipo de pruebas se deben también de incluir conjuntos de datos erróneos, para así verificar que el programa sabe que hacer que hacer en situaciones anómalas. En este caso, dependiendo del tipo de lenguaje que se este utilizando, se debe de lanzar algún tipo de excepción para gestionar el tipo de error causado en tiempo de ejecución.
  • Mantenimiento:
    • Actualmente, el mantenimiento de los programas y diversos sistemas es la actividad que mayor coste representa. El mantenimiento de nuestros programas se puede realizar de dos maneras:
      • Mantenimiento correctivo:
        • Este tipo de mantenimiento se da cuando hay situaciones o eventos que el programa o sistema no está resolviendo de manera adecuada.
      • Mantenimiento extensivo:
        • Este tipo de mantenimiento se refiere a extender las capacidades originales de nuestro programa o sistema, esto quiere decir, que nuestro programa o sistema debe regresarnos un conjunto adicional de resultados.
    • Para poder realizar un buen mantenimiento, es necesario incluir una buena documentación de nuestro programa, en caso contrario, sería imposible extender nuestro programa o sistema.
  • Extensión y refinamiento:
    • Esta etapa la realizan personas distintas a las que diseñaron originalmente el programa o sistema. En este caso, cuando se realiza la extensión de los programas o sistemas, lo que se busca es tratar de “tapar un hoyo, sin realizar otro en el programa o sistema”,  esto es fácil de realizar si se tiene modularidad en el programa o sistema, ya que es fácil atacarlo por módulos. Además debe tener la propiedad de encapsulamiento, esto es, que cada módulo tenga perfectamente delimitado su campo de acción,  esto permite tener una mejor comunicación entre módulos de manera controlada.
Estas etapas no necesariamente son realizadas de manera secuencial, ya que un buen trabajo de hace de manera cíclica entre los diversos pasos, esto para tener un software bien asentado, además de que pueden surgir problemas que conlleven a regresar a pasos anteriores que impliquen regresar a pasos anteriores para resolver dicho problema. Insisto que un buen diseño y análisis permiten tener éxito en etapas posteriores, sin necesidad de rehacer todo nuestro trabajo.

jueves, 6 de enero de 2011

El proceso del software (Análisis y diseño del algoritmo).

Análisis y diseño del algoritmo
Ahora que tenemos el enunciado del problema a resolver, así como las entradas con las que contamos y las salidas que deseamos, es necesario que tengamos un algoritmo que nos permita resolver nuestro problema y obtener los resultados que esperamos.
Para esto es necesario el modelar o elaborar un modelo que nos permita resolver nuestro problema. En más de una ocasión es probable que para llegar a nuestro objetivo se involucren una serie de pasos intermedios(estados de los datos), o más que ello, sea posible que necesitemos tener un cierto comportamiento para no tener un resultado concreto sino un resultado relativo que también nos sirve para nuestro objetivo. En el primer caso, me refiero a soluciones que necesitan ser inmediatos, como la impresión de un saludo en pantalla o resolver una operación aritmética, y, en el segundo caso, a cosas como los videojuegos o simulaciones (un reloj o un calendario por ejemplo).
A pesar de que depende del entorno de nuestro problema tendremos distintos tipos de soluciones que nos resolverán nuestro problema, cada solución tendrá distintas características propias de su entorno, pero, a pesar de ello todas las soluciones comparten un conjunto de características:


  • La solución debe de ser correcta, eficiente y efectiva.


Lo de correcta, creo que no hay nada que explicar sobre la correctez de una solución, ya que es lo que se desea cuando se diseña dicha solución. Con respecto a la eficiencia, se puede tener una excepción, ya que puede que solo si estamos haciendo un programa que nos ayude para resolver un problema para calcular algo o para sacarnos de un problema momentáneo, es decir, un programa que se va utilizar por un breve tiempo.
La eficiencia es una parte fundamental de una solución, ya que no podemos desperdiciar recursos(memoria principal, tiempo de procesamiento, etc.) de manera descuidada, ya que en más de una ocasión una pequeña falta de eficiencia en nuestras soluciones puede hacer que falle toda nuestra aplicación y la "tire".
Por efectiva nos referimos al hecho de que produce el efecto esperado en conjunción con la correctez y la eficiencia del mismo. Actualmente los programas son tan grandes que resulta muy difícil que una sola persona los realice por su propia cuenta, así que se tiende a trabajar en equipos(esto de manera muy GENERAL), entonces se requiere que los programas sean:


  • Modulares: esto quiere decir que se pueda dividir en varios pedazos (tener fronteras entre diversas partes que hagan su parte por separado), así para que las tareas se puedan repartir de manera eficiente.
  • Bajo nivel de acoplamiento: Esto es, que tengan que recibir o utilizar lo menor posible del exterior y entreguen lo mínimo posible (esto es que haya muy poco tráfico  entre los módulos), de tal forma que puedan ser reutilizados por otros.
  • Alta cohesión: aquí nos referimos al hecho de que todo lo que se encuentre relacionado (variables, métodos) se encuentren juntos, para que el programa sea fácil de entender y modificar.


Esto es lo básico del análisis y diseño del algoritmo, en el siguiente post seguiré con las demás etapas del proceso del software.

miércoles, 5 de enero de 2011

El proceso del software (especificación del problema).

Especificación del problema:
En la especificación del problema, se nos presenta el enunciado del problema a resolver, entonces debemos de entender el problema que tenemos que resolver, así como pedir toda la información necesaria que necesitemos para resolverlo, de tal manera que cuando nos encontremos en pasos posteriores del proceso de desarrollo, sea fácil y sin dificultad de entender lo que estamos desarrollando.
Dado que hemos entendido nuestro problema, es momento de determinar de donde partimos, es decir, con que entradas contamos para tratar de resolver nuestro problema, así como a donde queremos llegar (salidas deseadas, o resultados de nuestro problema).
En ésta etapa se tienen que tener bien claro los siguientes incisos:

  • Enunciado preciso del problema.
  • Entradas.
  • Salidas.

Y con esto tenemos nuestra especificación del problema, lo cual nos llevará a la siguiente etapa que es el análisis y diseño del algoritmo que nos permiirá resolver el problema planteado.
En el siguiente post les hablaré sobre el "Análisis y diseño del algoritmo".