February 2010 - Posts
La gente de la comunidad ALT.NET Hispano organiza para mañana sábado una reunión virtual, no conferencia, sino ALT.NET Café. La idea es presentar y probar el nuevo trabajo del bueno de @FabioMaulo, su proyecto ConfORM. Este es el anuncio:
El sábado 27 de febrero tendremos una reunión virtual. Será un Alt.Net Cafe virtual con el tema "ConfORM", con Fabio Maulo Fecha: Sábado 27 de Febrero a las 18:00 hrs. Hora Internacional (GMT/UTC), con una duración aproximada de 2 horas. Para atender la reunión deben usar el enlace: http://snipr.com/virtualaltnet el cual les demandará el uso del programa Microsoft Office Live Meeting 2007 Client. Para mas información sobre ConfORM vean el blog de Fabio http://is.gd/96pXs. Encontrará más información sobre lo necesario para atender la reunión en Descripcion de Reuniones VAN.
Qué es ConfORM? Pueden leer los post de Fabio en su blog:
Map NHibernate using your API
ConfORM: NHibernate un-Mapping
Toma un modelo de dominio YA compilado en una .dll, lo examina por reflection, como el del post:
y mediante agregados por código se construye el mapeo de NHibernate, sin usar archivos de configuración de XML. Ahora, Fabio está pidiendo modelos de ejemplo para probarlo, es parte de la idea de la reunión de mañana.
No entendí del todo la intención de este proyecto, enmarcado en una idea más grande que Fabio llama Orum (me temo que Fabio mencionó la idea en el post, pero no encontré en enlace donde la explica, la recuerdo de las discusiones de la lista). En particular, no conozco como generar XMLs, dice Fabio:
ConfOrm does not generate XML mappings but you can easy create XMLs to check how ConfOrm work.
Pueden bajar el código para probarlo desde:
http://code.google.com/p/codeconform/
Pueden leer la discusión en la lista de ALT.NET Hispano en:
Alt.Net Café sobre ConfORM
Ahí pueden leer como enviarle modelos de prueba a Fabio.
Hoy viernes, Fabio escribió:
mañana vamos a probar ConfORM... aún estan a tiempo de enviar algún domain (entidades).
No se necesitan los fuentes, con la DLL alcanza ustedes, pero, tengan los fuentes a mano y estas tres cosas:
- which is the strategy to represent a class hierarchy
- which is the relation between classes
- how manage cascade-actions (read it as: which are aggregate-root)
Pueden ver una discusión y ejemplo de su uso en:
Demo.cs - rgb-playground - Project Hosting on Google Code (acá parece que está el código para generar XML y ver el resultado del mapeo)
Modeling Files and Folders ala FileSystem - nhusers | Google Groups
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
El pasado año, escribí un ejemplo de web crawler usando mensajes, detalles en los posts:
Distributed Web Crawler using AjMessages
Web Crawler distribuido usando AjMessages
Antes, escribí otros ejemplos usando DSS/CCR, tecnologías incluidas en Microsoft Robotics Developer Studio:
Distributed Agents using DSS/VPL
Web Crawler example using DSS (Decentralized Software Services)
Aplicaciones Distribuidas con AjMessages usando DSS/CCR
Ejemplo de Web Crawler usando DSS
Ahora, escribí un ejemplo local (no distribuido) usando agentes en AjSharp. Recordemos, cada agente se ejecuta en su propio thread, y sus invocaciones son encoladas, y ejecutadas de una en una. Más sobre agentes en AjSharp en:
Agents in AjSharp (Part 1)
Agentes en AjSharp (Parte 1)
Agents in AjSharp (Part 2)
Agentes en AjSharp (Parte 2)
Hace dos días, tomé el código de mis anteriores ejemplos, y lo reensamblé en agentes de AjSharp. Este es el primer resultado de ese experimiento. Primero, definí un objeto para armara una red de agentes que se ocupen de visitar un sitio, y lanzar el proceso:
// Build and launch agents
object WebCrawler
{
sub Process(url, fn)
{
uri = new System.Uri(url);
downloader = new Downloader();
harvester = new Harvester();
resolver = new Resolver(uri,5);
processor = new Processor(fn);
downloader.Harvester = harvester;
downloader.Processor = processor;
harvester.Resolver = resolver;
resolver.Downloader = downloader;
downloader.Process(uri, 0);
}
}
El Downloader toma una URI, baja su contenido y envía el resultado a dos agentes asociados: Processor y Harvester. El parámetro Depth indica la profundidad de esta página en el proceso de crawling del sitio:
// Downloads a page
agent Downloader
{
sub Process(uri,depth)
{
client = new System.Net.WebClient();
content = client.DownloadString(uri);
PrintLine("Downloaded: " + uri);
this.Harvester.Process(uri,depth,content);
this.Processor.Process(uri, content);
}
}
The Processor executes a user function/routine, receiving the URI and its retrieved content:
// Process the content retrieved
agent Processor
{
function Processor(fn)
{
this.fn = fn; // function to invoke
}
sub Process(uri, content)
{
// Add your logic
this.fn(uri, content);
}
}
El Harvester detecta otros enlaces en el contenido, y los envía, uno a uno, a otro agente, el Resolver:
// Get links from page
agent Harvester
{
sub Process(uri,depth,content)
{
matches = System.Text.RegularExpressions.Regex.Matches(content, "href=\\s*\"([^&\"]*)\"");
results = new List();
foreach (match in matches) {
value = match.Groups[1].Value;
if (!results.Contains(value))
results.Add(value);
}
foreach (result in results)
if (result.StartsWith("http"))
this.Resolver.Process(new System.Uri(result), depth+1);
}
}
El Resolver mantiene una lista de URIs procesados, y filtra aquellas que no se encuentran en el host original. El proceso se limita entonces hasta una cierta profundidad de exploración y dentro del sitio original:
// Filter invalid or already processed links
agent Resolver
{
var processed = new List();
function Resolver(uri,maxdepth)
{
this.host = uri.Host;
this.maxdepth = maxdepth;
}
sub Process(uri,depth)
{
if (depth > this.maxdepth)
return;
if (uri.Host != this.host)
return;
if (uri.Scheme != System.Uri.UriSchemeHttp && uri.Scheme != System.Uri.UriSchemeHttps)
return;
if (processed.Contains(uri))
return;
processed.Add(uri);
PrintLine("New Link: " + uri);
this.Downloader.Process(uri,depth);
}
}
Estos son ejemplos de uso, creando dos redes de agentes, dedicados a obtener el contenido de dos sitios:
// Example
WebCrawler.Process("http://ajlopez.wordpress.com", function(uri,content) { PrintLine("From ajlopez.wordpress "+uri);});
WebCrawler.Process("http://ajlopez.zoomblog.com", function(uri,content) { PrintLine("From ajlopez.zoomblog "+uri);});
Pueden bajar el AjSharp desde el trunk:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjLanguage
El código de web crawler está en Examples/WebCrawler.ajs dentro del proyecto AjSharp.Console. Despues de compilar a este programa de consola, pueden lanzar:
AjSharp.Console Examples/WebCrawler.ajs
Esta es una salida parcial:
Pueden ver el ejemplo completo en Pastie http://pastie.org/835926
Próximo paso: usar agentes distribuidos. Hay dos caminos a explorar: uno, declarar que algunos agentes sean distribuidos usando código adicional o configuración, pero sin cambiar el código original. Dos, hacer la distribución explícita en el código.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
En mis experimentos con agentes, usando el intérprete AjSharp, encontré que me faltaba algo para implementar lo que quería: soportar una cantidad variable de parámetros en la llamada a una función, rutina o método. Así, que volví a codificar, con el sombrero de Test-Driven Development puesto, durante un desayuno de codificación, y salió lo que describo en este post.
Tuve que implementar dos facetas de esa característica. Primero, la declaración de recibir una cantidad variable de argumentos:
function FirstParameter(pars...)
{
if (pars.Length>0)
return pars[0];
return null;
}
function SecondParameter(pars...)
{
if (pars.Length>1)
return pars[1];
return null;
}
result = FirstParameter(1,2,3); // 1
result2 = SecondParameter(1,2,3); // 2
result3 = SecondParameter(); // null
La otra parta, qué pasa si quiero usar los parámetros recibidos con … (tres puntos) como parámetros para entregar a otra función? El pars… recibido es un arreglo de objetos. Para usarlo pasándolo no como arreglo, sino como los propios valores “expandidos”, usé el mismo operador … (tres puntos), pero ahora, desde la invocación:
function FirstParameter(pars...)
{
if (pars.Length>0)
return pars[0];
return null;
}
function SecondParameter(pars...)
{
if (pars.Length>1)
return pars[1];
return null;
}
function JustDoIt(fn,pars...)
{
return fn(pars...);
}
result = JustDoIt(FirstParameter,1,2,3); // 1
result2 = JustDoIt(SecondParameter,1,2,3); // 2
result3 = JustDoIt(SecondParameter); // null
Curiosamente, hace dos días, encontré el lenguaje
CoffeeScript
que compila a JavaScript normal. En la página de tutorial, encontré un término nuevo para mí : Splats para denominar el operador … (tres puntos). Decidí usar ese operador hace unas semanas, y ahora me lo encuentro ahí. No recuerdo cuándo fue que se sembró en mi mente la idea de usar ese operador.
Podría emplear este operador en algún ejemplo de agente.
Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Estuve implementando una especie de soporte de agentes en mi intérprete AjSharp. En el anterior post:
Agents in AjSharp (Part 1)
Agentes en AjSharp (Parte 1)
describí algo de esa implementación y las principales características. Dos puntos para recordar:
- Cada agente ejecuta en su propio thread
- Cada agente tiene una cola de proceso para recibir sus invocaciones (método y argumentos)
Quiero mostrar en este post dos simples ejemplos de uso. Primero, uno donde los agentes se encadenan, implementando algo así como message passing, procesando un dato y produciendo resultados.
agent IncrementAgent
{
function IncrementAgent(next, channel)
{
this.channel = channel;
this.next = next;
}
sub Process(value)
{
newvalue = value + 1;
if (channel)
channel <- newvalue;
if (next)
next.Process(newvalue);
}
}
channel = new Channel();
thirdAgent = new IncrementAgent(null, channel);
secondAgent = new IncrementAgent(thirdAgent, channel);
firstAgent = new IncrementAgent(secondAgent, channel);
firstAgent.Process(0);
for (k = 1; k <= 3; k++)
result = result + <-channel;
// result == 1 + 2 + 3 == 6
Vean que cada agente es un eslabon de la cadena de proceso, y que se usa un channel para coleccionar los resultados. La combinación de colaboración de objetos se puede hacer en formas más complejas.
En el segundo ejemplo, implementé el problema de Collatz usando una ronda de dos agentes:
if (number % 2)
{
this.Next.Process(number, list);
return;
}
list.Add(number);
number = number / 2;
this.Next.Process(number, list);
}
}
agent OddAgent
{
sub Process(number, list)
{
if ((number % 2)==0)
{
this.Next.Process(number, list);
return;
}
list.Add(number);
if (number == 1)
{
this.Channel <- list;
return;
}
number = number * 3 + 1;
this.Next.Process(number, list);
}
}
channel = new Channel();
even = new EvenAgent();
odd = new OddAgent();
even.Channel = channel;
even.Next = odd;
odd.Channel = channel;
odd.Next = even;
even.Process(22, new List());
result = <-channel;
// result = { 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1 }
Más sobre la interesante conjetura de Collatz:
Collatz 3n+1 Problem Structure
Programming 3n+1 problem
Estos son ejemplos simples. Quiero implementar un web crawler, o un web scraping, o un algoritmo genético en paralelo, usando agentes. También tengo como próximo paso: agentes distribuidos.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
En estos días, agregué lo que llamo default methods en AjSharp, mi intérprete de código abierto. ¿Qué es un default method en la jerga de AjSharp? Me inspiré por el mensaje #doesnotunderstand que está en los objetos Smalltalk. AjSharp es un intérprete dinámico, así que podemos invocar cualquier método, aún uno no existente. Si el método no está definido, pero hay un default method declarado, entonces éste es invocado, recibiendo dos parámetors, el nombre del método original, y sus argumentos.
Primer ejemplo:
object Incrementor
{
function Increment(n)
{
return n+1;
}
}
class Proxy
{
var Object;
function Proxy(obj)
{
this.Object = obj;
}
default function InvokeMethod(name, parameters)
{
return this.Object.Invoke(name,parameters);
}
}
proxy = new Proxy(Incrementor);
result = proxy.Increment(3); // result == 4
La palabra clave default marca el default method (estoy aún reluctante a usa una palabra clave para esto, pero no tengo todavía otra idea para implementarlo). Notemos que esta implementación de proxy solamente funciona encapsulando instancias de DynamicObject, que tienen un méetodo .Invoke. Podría extenderlo para que maneje objetos CLR también.
Por ahora, otro ejemplo sencillo:
class Incrementor
{
function Increment(n)
{
return n+1;
}
}
class LoadBalancer
{
var objects;
var random;
function LoadBalancer()
{
this.random = new System.Random();
this.objects = new List();
}
sub Add(obj)
{
this.objects.Add(obj);
}
default function InvokeMethod(name, parameters)
{
n = random.Next(objects.Count);
return this.objects
.Invoke(name,parameters);
}
}
balancer = new LoadBalancer();
balancer.Add("Foo");
for (k=1; k<=10; k++)
balancer.Add(new Incrementor());
result = 0;
for (k=0; k<4; k++)
result = result + balancer.Increment(k);
// result == 1 + 2 + 3 + 4 == 10
Es una especie de “load balancer”, que usa al azar, podría llamarlo “distributor”. La instancia LoadBalancer envía cada llamada a método a uno de sus objetos, al azar.
Podría escribir otro ejemplo, donde el balancer envía cada invocación a TODOS sus objetos. Tengo pensado usar estas características en agentes, en especial, en agentes distribuidos. Podría agregar default methods a objetos AjSharp (recuerde, AjSharp puede definir objetos sin clase asociada).
Como siempre, el código está disponible en:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjLanguage
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Comencé a implementar ideas de agentes en mi intérprete AjSharp. Ya había explorado channel2, queue channels y futures en anteriores posts:
Channels and GoRoutines in AjSharp (Part 1)
Channels and GoRoutines in AjSharp (Part 2)
GoRoutines y Canales en C#
GoRoutines and Channels in C#
AjSharp: Implementing Futures
AjSharp: Implementando Futures
Queue Channels in AjSharp
Queue Channels en AjSharp
y una implementación paralela de un algoritmo directamente en C#:
Genetic Algorithms using GoRoutines and Channels in C#
Algoritmos Genéticos usando GoRoutines y Channels en C#
pero sería interesante tener un modelo más claro para la computación en paralelo. En programas no triviales, el manejo de varios canales puede ser “convoluted”, como mostré en la implementación del algoritmo genético. Así que extendí el lenguaje para soporte objetos similares a agentes, en dirección a un actor model parcial, con declaraciones como:
agent MyAgent {
// ....
}
Una definición de agente es como la definición de una clase. Podemos crear agentes usando el operador new y constructores:
agent = new MyAgent(parameter);
La diferencia con un objeto es: cuando una instancia de agente es creada, se lanza un background thread para procesar una cola, donde se van guardando las llamadas a métodos del agente y sus parámetros. Esas llamadas son procesadas por el thread el propio agente. Un ejemplo:
agent IncrementAgent
{
sub Process(channel, value)
{
channel <- value + 1;
}
}
myagent = new IncrementAgent();
channel = new Channel();
myagent.Process(channel, 1);
result = <-channel; // result == 2
La llamada myagent.Process(..) no se ejecuta en el thread principal, sino que es guardada en el queue interno del agente, y será procesada por el thread propio del agente. En la actual implementación, la queue interna es un queue channel, así que la sincronización entre invocadores y el agente es automática.
Para implementar este tipo de agentes, agregué la clase AgentFunction, representado el método a llamar. Cuando se lo invoca, no se ejecuta directamente sino que su invocación es enviada a la cola del agente:
public object Invoke(IBindingEnvironment environment, object[] arguments)
{
AgentObject agent = (AgentObject)((ObjectEnvironment)environment).Object;
agent.SendInvoke(this.function, environment, arguments);
// TODO if function, return a Future
return null;
// Old direct code
// return this.function.Invoke(environment, arguments);
}
La llamada SendInvoke pone los datos de la invocación en la cola de proceso del agente.
Hay una AgentClass, derivada de DynamicClass (que es la definición normal de una clase dinámica en AjSharp), extendida con una nueva manera de crear una instancia:
public override object NewInstance(object[] parameters)
{
AgentObject dynobj = new AgentObject(this);
this.NewInstance(dynobj, parameters);
dynobj.Launch();
return dynobj;
}
La clave está en dynobj.Launch() que lanza el thread de proceso que ejecutará todas las llamadas al agente, tomando sus datos de la cola interna.
De esta forma, llamar a un método de un agente recuerda a una message passing invocation. Si recordamos Smalltalk, una llamada es en realidad un mensaje con nombre y argumentos. En mi opinión, al usar la convención de <agente>.<método>(<parametros>) es una forma clara y transparente de implementar un message passing usando sintaxis “normal”.
Esta es la actual implementación ingenua del agente:
public class AgentObject : DynamicClassicObject
{
// TODO 100 is hard coded
private QueueChannel channel = new QueueChannel(100);
public AgentObject(IClass objclass)
: base(objclass)
{
}
public IChannel Channel { get { return this.channel; } }
public void Launch()
{
Thread thread = new Thread(new ParameterizedThreadStart(this.Execute));
thread.IsBackground = true;
thread.Start(Machine.Current);
}
public void SendInvoke(ICallable function, IBindingEnvironment environment, object[] arguments)
{
AgentTask task = new AgentTask() { Callable = function, Environment = environment, Arguments = arguments };
this.channel.Send(task);
}
private void Execute(object parameter)
{
Machine machine = (Machine) parameter;
machine.SetCurrent();
while (true)
{
object obj = this.channel.Receive();
AgentTask task = (AgentTask) obj;
task.Callable.Invoke(task.Environment, task.Arguments);
}
}
}
internal class AgentTask
{
internal ICallable Callable;
internal IBindingEnvironment Environment;
internal object[] Arguments;
}
Como siempre, el código está publicado en:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjLanguage
Próximos pasos:
- Explorar el proceso de agente con un ejemplo. Podría portar el ejemplo de algoritmo genético que escribí en C# o anteriores ejemplos míos de web crawler.
- Soportar la invocación de funciones que devuelvan valores en un agente: el resultado de retorono sería un future, que sería llenado por el agente en paralelo. Problemas: estaríamos en algún momento esperado el valor del future, con posibles dead-locks.
- Algo más ambicioso: agentes distribuidos. Que el new o algo similar, provoque que el agente se ejecute en otra máquina, y nosotros lo manipulamos como siempre a través de un proxy.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
La comunidad de Smalltalk es una comunidad activa, pero dispersa en varias listas, que tratan distintos dialectos y frameworks. Mariano Martinez Deck anunció hace unos días un nuevo grupo de Smalltalk. Esta es su presentación (disculpen que no traduzca):
We were discussing about this in the Pharo mailing list but the idea is for all Smalltalkers. I am not sure if this will work, but it is worth to try it as I don't have anything to loose.
We were thinking to have a place, an environment and hopefully a community, where we can freely discuss about Smalltalk papers or OO in general. All smalltalkers of all flavors can join, discuss, tell their experiences and thoughts, share and learn.
The main purpose about that list is:
- be able to FREELY discuss about papers about Smalltalk or OO in general. I say freely because maybe someone gives an opinion of a paper that the person who wrote it is also in this place. We are all professional and I think we can discuss with respect.
- LEARN.
Other uses:
- Don't reinvent the wheel. Maybe I wanted to do something and I read that XXX person did YYY. So, I can look at it.
- Be aware of what other people is doing, working, writing and learning.
- Have different opinions of a certain topic / paper.
- A little step to join all the Smalltalk community in one place. We can meet people, join forces, etc.
- Help in the preparation, ANN, collaborate, etc in Workshops, conferences, and so on.
- Ask for help on review. Several times someone has several papers to review for a certain conference. This can be a place to ask for that help.
- Publish papers that were rejected from someone. How many times some papers where rejected but do you still read it and find it useful ?
- Educate people and being educated.
- Share latex template, commands, or tricks related to smalltalk code for example.
- Discuss about research in general.
- Ask for a certain topic. Suppose I want to start to work in XXX topic, I can ask to see if someone knows related papers or work.
- others
In two words: DISCUSS AND LEARN.
FAQ:
1) Which is the address?
http://groups.google.com/group/smalltalk-research
2) Do I need to be "Researcher" to join ?
Not necessary. You may just be interested.
3) Is there a Nabble interface?
Yes: http://n4.nabble.com/Smalltalk-Research-f1473466.html
Now, please what I would appreciate is if you can help me to you distribute (I am not in Gemstone, Cincom or GNU for example) this ANN. It would be cool to create a community. I know there are a lot of Smalltalk researchers that doesn't have time to follow the active smalltalks lists. If you can send private mails to them would be fantastic!
Hay una activa comunidad de Smalltalk, acá en mi país Argentina. Espero que organizaran con éxito la reunión Smalltalk de este año 2010, después de haber armado las de 2007, 2008, 2009. Espero también, una mejor comunicación y transferencia de conocimiento con otras comunidades de desarrollo de software. En mi opinión, los smalltalkers están muy “cerrados” en sí mismos: hay tantos tópicos interesantes para compartir y discutir, en el mundo Smalltalk y más allá. Noten el uso generalizado de Virtual Machines (como las de Java y .NET) como base de nuevos lenguajes. Sería bueno tener más “polinización cruzada” entre comunidades de desarrollo e ideas.
Mientras, pueden leer la interesante discusión sobre el uso de la memoria en Smalltalk, en el nuevo grupo anunciado por Mariano:
LOOM and Virtual Memory
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Hace más de un año, escribí:
Algoritmos Genéticos con AjAgents y Concurrency and Coordination Runtime (CCR)
Genetic Algorithms with AjAgents and Concurrency and Coordination Runtime (CCR)
explorando la concurrencia en la implementación de una solución al Travelling Salesman Problem usando un algoritmo genético. Al final del año pasado, escribí un ejemplo, usando lo que ya había implementado en C#:
GoRoutines y Canales en C#
GoRoutines and Channels in C#
El código de este experimento está en el proyecto AjConcurr.Tsp en el repositorio:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjConcurr
Este es el resultado (no es una gran interfaz… ;-):
TSP no es un buen problema para resolver usando algoritmos genéticos: el resultado depende de la mejora de la población de soluciones tentativas después de cada iteración. En esta implementación uso un algoritmo de mutación (no estoy usando crossover) que intenta generar mejores individuos (en general, un algoritmo de mutación debería solamente cambiar al individuo, dejando la emergencia de mejoras al algoritmo genético mismo). El código está escrito en un gran método, que se lanza desde el botón Run. Es código sólo para demostración de la idea.
Al comienzo de ese método, se definen los canales a usar:
Channel genomas = new Channel();
Channel evaluated = new Channel();
Channel bestsofar = new Channel();
Channel mutator = new Channel();
Recordemos: podemos poner un valor (un objeto cualquiera) en un channel, y en otro thread, podemos obtener el valor. Si colocamos un valor en un channel sin que haya otro thread que está esperando ese valor, nuestro thread se bloquea. Si tratamos de leer el próximo valor de un channel, y no hay tal valor, nuestro thread se bloquea. De esta forma, productores y consumidores de valores se van sincronizando.
El canal Evaluated recibe individuos (soluciones del problema) y los collecciona, formando una nueva población. El mejor individuo es enviado al canal bestsofar, que mantiene cuál es el mejor individuo hallado hasta el momento en el proceso. El canal genomas recibe un individuo, evalúa la solución que representa, y lo reenvía al canal evaluated. El canal mutator recibe un genoma, lo muta y evalúa. El nuevo genoma es entonces reenvíado al canal evaluated.
De esta forma, varias poblaciones son procesadas en paralelo. Para ser más exacto, no hay el concepto de población, sino que hay N * M individuos en proceso. Por cada N de esos individuos que llegan al final del proceso, los mejores son seleccionados, y un nuevo grupo de N individuos es inyectado de nuevo en el proceso.
Veamos el código, compuesto de varias GoRoutinas. Primero, lanzamos una goruotine para generar la poblaciones iniciales:
// Generates the initial populations
GoRoutines.Go(() =>
{
Genoma genoma = GenerateGenoma();
for (int j = 0; j < PopulationSize * 10; j++)
{
genomas.Send(this.Mutate(genoma));
}
});
Se lanza una goroutine para evaluar nuevos genomas:
// Evaluate genomas
GoRoutines.Go(() =>
{
while (true)
{
Genoma genoma = (Genoma)genomas.Receive();
genoma.value = this.CalculateValue(genoma);
evaluated.Send(genoma);
}
});
Una goroutine para coleccionar genomas, seleccionarlos, y generar un nuevo grupo, población:
// Collect and select
GoRoutines.Go(() =>
{
GenomaComparer comparer = new GenomaComparer();
while (true)
{
List<Genoma> genomalist = new List<Genoma>();
for (int k = 0; k < PopulationSize; k++)
{
Genoma genoma = (Genoma)evaluated.Receive();
genomalist.Add(genoma);
}
genomalist.Sort(comparer);
GoRoutines.Go(() => bestsofar.Send(genomalist[0]));
for (int k = 0; k < PopulationSize / 5; k++)
GoRoutines.Go(() => evaluated.Send(genomalist[k]));
for (int k = 0; k < PopulationSize / 5; k++)
{
GoRoutines.Go(() => mutator.Send(genomalist[k]));
GoRoutines.Go(() => mutator.Send(genomalist[k]));
GoRoutines.Go(() => mutator.Send(genomalist[k]));
GoRoutines.Go(() => mutator.Send(genomalist[k]));
}
}
});
A goroutine for receives genomas, improve them, and forwards them to evaluated channel:
// Mutates
GoRoutines.Go(() =>
{
Random rnd = new Random();
while (true)
{
Genoma genoma = (Genoma)mutator.Receive();
Genoma newgenoma = this.Mutate(genoma);
while (newgenoma.value >= genoma.value)
{
if (rnd.Next(3) == 0)
break;
newgenoma = this.Mutate(genoma);
}
evaluated.Send(newgenoma);
}
});
Una goroutine para recibir y procesar los mejores encontrados hasta el momento:
// Receives and draws the results
GoRoutines.Go(() =>
{
Genoma best = null;
while (true)
{
Genoma genoma = (Genoma)bestsofar.Receive();
if (best == null || best.value > genoma.value)
{
best = genoma;
this.BestGenoma(genoma);
}
}
});
No puse una operación de crossover. El caso es que el problema y el algoritmo podrían cambiar, pero la idea es explorar la implementación paralela de un algoritmo genético.
El principal problema que encontré es en el proceso de una población acumulada. En ese paso, la goroutine tiene que enviar varios valores a varios canales. Si alimenta un canal, y ésto la bloquea, los demás canales no recibirán sus valores. Tuve que lanzar varias goroutines para solucionar este problema, cada una alimenta a un canal.
Próximos pasos: implementar el algoritmo usando Queue Channels como en:
Queue Channels en AjSharp
e implementar un actor model, colocando cada paso en un agente, no en una goroutine. Llegados a ese punto, podría intentar poner los agentes en distintas máquinas.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Una de las características que quería tener desde el principio en mi intérprete AjSharp era que las funciones y rutinas sean ciudadanos de primera clase en el lenguaje (en este post uso la palabra clave function pero se puede usar también sub para definir una subrutina).
El código está en el proyecto de código de Google:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjLanguage
AjLanguage es el lenguaje núcleo (que no tiene ni parser ni lexer, sólo lo esencial del intérprete). AjSharp es el parser y el lexer implementado sobre el AjLanguage. De esta forma, podría implementar AjBasic u otros lenguajes similares, usando AjLanguage como base y núcleo.
En AjSharp, podemos definir funciones como en otros lenguajes:
function Square(n) { return n*n; }
Podemos definir una función anónimca y asignarla a una variable:
MySquare = function (n) { return n*n; };
Un functional value puede ser usado como parámetro:
function Apply(func,values)
{
list = new List();
foreach (value in values)
list.Add(func(value));
return list;
}
numbers = new List();
numbers.Add(1);
numbers.Add(2);
numbers.Add(3);
function Square(n) { return n*n; }
squared = Apply(Square, numbers);
squared2 = Apply(function (n) { return n*n; }, numbers);
La función Apply definida arriba, toma una función, una lista, y retorna otra lista de elementos que resultan de evaluar f(e), donde f es la función recibida, aplicada a cada elemento e de la lista que se pasó como parámetro. Podemos invocar Apply con el nombre de la función (que en realidad es el nombre de la variable que contiene al functional value) o definidiendo la función directamente en la invocación.
Estoy pensando sobre algunas alternativas de implementación del alcance y visibilidad de variables en expresiones funcionales (dará para otro post el tema). Si definimos una función, AjLanguaje (el núcleo de AjSharp) usa una closure (clausura), así que cuando la función es invocada, el binding environment original es usado. En los ejemplos de más abajo se usa esa característica.
Cada función implementa:
public interface ICallable
{
int Arity { get; }
IBindingEnvironment Environment { get; }
object Invoke(IBindingEnvironment environment, object[] arguments);
object Invoke(object[] arguments);
}
esto es, cuando es invocada, recibe un binding environment (un diccionario que dado el nombre de una variable nos da su valor asociado). Un funcional value no usa ese binding environment recibido, y lo reemplaza por el environment original que tenía en el momento de haber sido definida, creada.
Podemos retornar una función como retorno de una función:
function MakeIncrement(x)
{
return function(n) { return n + x; }; // x is bounded to local parameter
}
Increment2 = MakeIncrement(2);
result = Increment2(2);
// result == 4
Increment3 = MakeIncrement(3);
result2 = Increment3(2);
// result2 == 5
result3 = MakeIncrement(4)(3);
// result3 == 7
x = 4;
result4 = function(n) { return n+x; }(5);
// result4 == 9
MakeIncrement retorna una función que usa y accede a la variable x, ligada en el binding environment de llamada.
En el último comando, la función es definida e invocada en el mismo comando.
AjSharp tiene clases, y podemos definir funciones y rutinas como métodos:
class Person
{
var Name;
var Age;
function AddYears(years)
{
this.Age = this.Age + years;
}
}
Pero también podemos agregar funciones en cualquier momento:
class Person
{
var Name;
var Age;
function Person()
{
x = 100;
this.GetYears = function () { return this.Age; };
this.GetX = function() { return x; };
}
}
adam = new Person() { Name = "Adam", Age = 800 };
result = adam.GetYears();
// result == 800
result2 = adam.GetX();
// result2 == 100
o agregar funciones/rutinas directamente a objetos:
adam.AddYears = sub(n) { this.Age = this.Age + n; };
Hace unas semanas, leí el post:
Functional Programming in Javascript
de James Carr, así que decidí probar las capacidades de AjSharp para manejar functional values adaptando algunos de esos ejemplos, como:
function runningSum(start){
sum = start; // you could use var sum, it's local
return function(a){
sum = sum + a; // function access the "outer" sum
return sum;
};
}
sum = runningSum(3); // makes function
result = sum(2); // returns 5
result2 = sum(10); // returns 15
Podemos usar directamente el parámetro como acumulador:
function runningSum(start){
return function(a){
start = start + a; // function access the "outer" start
return start;
};
}
sum = runningSum(3); // makes function
result = sum(2); // returns 5
result2 = sum(10); // returns 15
Agregué soporto para aplicar una función como si fuera un método a un objeto, como en uno de los ejemplos de Carr (este tipo de llamada es soportada en Javascript que tiene la clase Function):
person = new { Name = "Adam", Age = 800 };
GetAdjustedAge = function (x) { return x + this.Age; };
result = GetAdjustedAge.Call(person, 10); // result == 810
Próximos pasos: estabilizar el alcance de las varaibles y el acceso al environment global. Por ahora, me divertí bastante implementando valores funcionales
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez