RAGE : Reckoner AGEnts

An easy toolkit for distributed computing

DOWNLOAD RAGE

Introduction

Rage is an easy to use framework for distributed computing. It allows to develop, distribute and run calculus on heterogeneous framework with no need of background in distributed computing or agents technologies. The user can only direct its efforts towards his very calculus.

Rage was buit to show that multi-agent paradigms: agent animacy, organization and roles, are key notions to support the analysis, design and implementation of open large-scale distributed systems. It is particulary significant that those application models are in adequation with multi-agent paradigms.

User point of view on Rage

Rage wants to be a framework that offers an easy development of distributed calculus, that allows multi-applications computing in parallel and that can scale with the available power (clustering).

Simplicity was a preliminary condition for the framework. The main idea was to provide an easy framework for non computer scientists. To achieve this goal, we had to define a small number of concepts that the user has to understand in order to feed the system with its calculus. It is important to note that the end user has no need to know anything about agent or multi-agent systems, even if he has to understand at least some object-oriented notions (since he must inherits some predefined patterns).

The user has mainly to know two concepts :

A task can be seen as the algorithm that is distributed, while a result represents the data produced by the task and which must be stored.
The user of the framework has to define what should be distributed, and what is the global scheduling of its main algorithm. The task defines the chunk of algorithm that will be distributed among agents. The simplest way for the user to create a task is to subclass the AbstractTask class and to define the only two methods:

abstract public void compute();
abstract public boolean finished();

The computation has to follow this scheme, so the agent that has to compute the task can do :

while(!finished() && !notInterrupted) { compute(); }

The complexity for the user is then not bigger than writing a Java Applet.

Therefore, the end user has a rather OO view of the framework: he extends one class to tailor it to his distributed application and uses the underlying framework without necessary explicitly knowing what happens.

Developer point of view on Rage

Rage has been developed with the Magique toolkit. The framework is composed by several kind of agents :

In fact agents in Magique, agents are all the same : they're empty shells. Then the skills that we provide them, tailor them to their task. Thus, it isn't agents that are described above, but more precisely roles, which are concretized as skills. Besides agent's abilities, an organization of those agents is necessary to provide a default communication path between them. The hierarchy for Rage is the following :
Rage hierarchy
Except the TaskManager which is on the "client side", all others agents belongs to the framework. When a task is sent by the TaskManager to the Boss, it is forwarded to the TaskDispatcher, which send them back on request to PlaformManagers. Once tasks are available to PlatformManagers, they are dispatched to ReckonerAgents that compute them. When computations are over, results are stored thanks to the ResultRepository. Then, clients can query the Boss to retrieve results.

Tutorial example

This example illustrate how a simple computation can be distributed with Rage. The aim is to compute an approximation of PI, using a Monte-Carlo method. Here is the implementation of the task :

public class PiTask extends AbstractTask {

    public Double pi;

    private int currentIter;
    private int total;
    private int inner;    
    private int niter;

    public PiTask(String factoryId, String taskId, int niter) {
        super(factoryId, taskId);
        total = 0;
        inner = 0;
        this.niter = niter;
    }

    public void compute() {
        double x, y, tmp;
        int n = 0;

        for(currentIter = 0; currentIter < niter; currentIter ++) {
            x = Math.random();
            y = Math.random();

            if (java.lang.Math.sqrt(x*x + y*y) <= 1.0)
                inner ++;
        }

        tmp = (double)inner / (double)niter;
        tmp *= 4;

        pi = new Double(tmp);
    }

    public boolean finished() {
        return currentIter == niter;
    }

}

This simple example illustrate how a task should be designed :

That is all that has to be done concerning tasks and results.

Now, the following source code illustrates how to retrieve results :


    public void getPiResult() {
        Set theResults = (Set) askNow("getAll");
        int nres = theResults.size();
        double pi = 0;

        if (nres == 0) {
            gui.setInfo("No result found in repository");
            return;
        }

        gui.setInfo(nres + " results found");

        Iterator it = theResults.iterator();
        while(it.hasNext()) {
            Result res = (Result) it.next();
                   
            Double d = (Double) res.get(Double.class, "pi");
            pi = pi + d.doubleValue();
        }
        pi = pi / nres;
        gui.setInfo("Result PI = " + pi);
    }


In this example, all results are retrieved, using the getAll method, but it is possible to fetch results individually.

More examples can be found in the distribution : enjoy :)