Stream Agents reference
Stream Agents is a module providing a general multi-agent system that is a base for various more concrete implementations of agent-based computations. Currently, besides the base code, it offers a base implementation for Evolutionary Multi-Agent Systems.
Stream Agents extensively use immutable collections and utilities from Vavr. You need to keep that in mind when performing actions on collections returned from methods. Moreover, all objects are immutable if not mentioned otherwise.
Basic concepts
- Agent (
Agent
interface) is just a serializable container for data and operations. - Workplace (
Workplace
class) is a threaded context for a population of agents. - Pipeline (
Pipeline
class) is a stream-based definition of operations to execute on a population of agents. - Environment (
Environment
class) is a view of the system provided to agents.
Workplace
Workplaces are main executors for the Stream Agents computation. Each workplace executes in a separate thread and works asynchronously in relation to others.
Workplace operation is defined by the following operators:
- before step action,
- step,
- after step action.
Moreover, each workplace has an id nad an initial population of agents.
Before step action is defined as a function of three arguments:
- step number,
- current population,
- incoming population (coming from migrations).
And returns a population for the current step (usually just a merge between current and incoming agents).
Step is a main computation operation that should contain code that changes population. It receives:
- current step number,
- current population (returned from the before step action)
- reference to the environment.
After step action is usually used to collect statistics. It receives the following arguments:
- id of the workplace,
- step number,
- current population (generated by the step).
And it returns the map of statistics.
Environment
Environment is the only view of the global computation accessible to agents and operators. It is a way for them to gain the limited knowledge of the world and perform interactions with other populations.
Environment makes it possible to:
- gain the knowledge about workplaces (id and count of workplaces, list of neighbour workplaces),
- gain the knowledge about other populations statistics (`workplaceStatistics)
Pipeline
Pipeline defines a stream-like, functional operation on the population of agents.
The base class is pl.edu.agh.age.compute.stream.Pipeline
,
however it is expected that more concrete computation types will provide more adequate implementations.
Basically, the concept behind pipeline is to provide a functional and easy way to define transformations over the population. For example:
final Tuple2<PairPipeline, Pipeline> pipelines = Pipeline.on(population).selectPairs(selector);
// reproduction
Tuple2<Pipeline, Pipeline> reproduced = pipelines._1.reproduce(reproduction);
Tuple2<Pipeline, Pipeline> selfReproduced = pipelines._2.selfReproduce(selfReproduction);
Pipeline parentAgents = reproduced._1.mergeWith(selfReproduced._1);
Pipeline childAgents = reproduced._2.mergeWith(selfReproduced._2);
// fight
parentAgents = parentAgents.fight(fight);
// evaluate
childAgents = childAgents.evaluate(evaluator);
// merge and extract
List<EmasAgent> population = parentAgents.mergeWith(childAgents).extract();
Pipelines are eagerly evaluated. It is currently required in order to support splitting populations.
Stop condition
Stop condition (StopCondition
interface) defines conditions under which the computation is gracefully stopped.
Currently, only time-based stop condition implementation is provided.
Stop condition is global for the whole computation. If it is fulfilled, all of the workplaces initiate shutdown.
Evolutionary Multi-Agent Systems
Note: To get the overview of the EMAS model, you should read one of our publications. This documentation contains only a technical description of the implementation.
EMAS API and implementation is located in the pl.edu.agh.age.compute.stream.emas
package.
Agent and solutions
The agent class is EmasAgent
.
Each agent has the following properties:
- id,
- energy,
- solution.
The equality of agents is based on their id.
Energy is a simple double value.
Solution is defined in the interface pl.edu.agh.age.compute.stream.emas.solution.Solution
.
It is a wrapper over a particular solution value (for example, for a double
number) with a fitness value attached.
However, it is not required for the fitness to be present (in that case NaN
will be returned).
A concrete implementation of the solution will be dependent on the computation, but some of the most commonly used types are already provided:
DoubleSolution
– a single double value.DoubleVectorSolution
– a vector of double values.SimpleSolution
– a wrapper over any object.
Pipeline
The pipeline for the EMAS provides some dedicated operations:
-
Tuple2<PairPipeline, Pipeline> selectPairs(BiFunction<EmasAgent, List<EmasAgent>, Tuple2<EmasAgent, EmasAgent>> selector)
Selects pairs of agents using the given selector. Returns aPairPipeline
for the paired agents and aPipeline
for non-paired ones. The latterPipeline
can be either empty (in case of even agents count) or contain a single agent (for odd agents count). -
PairPipeline selectPairsWithRepetitions(BiFunction<EmasAgent, List<EmasAgent>, Tuple2<EmasAgent, EmasAgent>> selector)
Selects pairs of agents using the given selector but allows a single agent to be used in more than one pair. This method ensures that every agent is paired, even if the agents count is odd. -
Tuple2<Pipeline, Pipeline> selfReproduce(Function<EmasAgent, Tuple2<EmasAgent, EmasAgent>> selfReproductionStrategy)
Self reproduces agents from the pipeline applying a given strategy. Self reproduction is applied to every agent in the pipeline and produces one child for each agent. -
Tuple2<Pipeline, Pipeline> selfReproduce(Predicate<EmasAgent> selfReproductionPredicate, Function<EmasAgent, Tuple2<EmasAgent, EmasAgent>> selfReproductionStrategy)
Performs self reproduction for agents that meet the given predicate. -
Pipeline evaluate(PopulationEvaluator<EmasAgent> populationEvaluator)
Evaluates all agents from the pipeline at once. Note thatPopulationEvaluator
can also perform additional operations on the population, such as local optimization (improvement). -
Pipeline process(Function<Seq<EmasAgent>, Seq<EmasAgent>> populationProcessor)
A way to perform custom operations (e.g. operations that are not implemented in the Pipeline API) on the whole population at once. -
Tuple2<Pipeline, Pipeline> migrateWhen(Predicate<EmasAgent> migrationPredicate)
Chooses agents for migration using the given predicate. -
Tuple2<Pipeline, Pipeline> dieWhen(Predicate<EmasAgent> deathPredicate)
Chooses agents that should die using the given predicate.
migrateWhen
and dieWhen
are identical in behavior but they are differentiated for better semantics.
Both return two separate pipelines: one for selected and one for unselected agents.
PairPipeline
The paired agents are represented by the PairPipeline
, which allows performing operations that require two agents.
Currently, these are supported:
-
Tuple2<Pipeline, Pipeline> reproduce(Function<Tuple2<EmasAgent, EmasAgent>, Tuple2<Seq<EmasAgent>, EmasAgent>> reproductionStrategy)
Reproduces a pair of agents using a given sexual reproduction strategy. Returns the tuple of parent and child agents. See Reproduction section for details about the reproduction. -
Pipeline fight(Function<Tuple2<EmasAgent, EmasAgent>, Seq<EmasAgent>> fightingStrategy)
Performs a fight between agents using a given strategy. See Fighting section for details about the fight. -
Tuple2<Pipeline, Pipeline> encounter(Predicate<EmasAgent> reproductionPredicate, Function<Tuple2<EmasAgent, EmasAgent>, Tuple2<Seq<EmasAgent>, EmasAgent>> reproductionStrategy, Function<Tuple2<EmasAgent, EmasAgent>, Seq<EmasAgent>> fightingStrategy)
Performs an agents encounter action, which combines sexual reproduction and fighting in one method. When a given reproduction predicate is met for a pair of agents – they reproduce themselves (using a given reproduction strategy). Otherwise, they fight which each other (using a given fight strategy).
Reproduction
Reproduction is provided by the pl.edu.agh.age.compute.stream.emas.reproduction
package.
Currently, sexual reproduction and asexual reproduction are implemented.
Both reproduction types are also based on a pipeline concept but it is usually better to prepare the pipeline beforehand using provided builders:
SexualReproductionBuilder
or AsexualReproductionBuilder
.
The reproduction is built using the following operations:
- recombination (required),
- mutation (optional),
- energy transfer (required).
Operations are executed in the same order as specified in the list above. Recombination and mutation operate on solutions while energy transfer modifies agents’ energy.
The sexual reproduction operates on a pair of agents whereas the asexual reproduction - on a single agent. Asexual reproduction has already built-in recombination operator which duplicates the parent agent as its child, leaving only an optional mutation and a compulsory energy transfer to be defined manually.
Both reproduction types always generate one or two parent agents and a single child. Please note that the parents returned from the pipeline usually have energy levels changed by the energy transfer operator.
Fighting
Fighting is located in the pl.edu.agh.age.compute.stream.emas.fight
package.
It is much simpler than reproduction, as it only performs two operations:
- comparison of two agents (using Comparator),
- energy transfer.
The fight generates a sequence of agents – the same agents that were passed to it, but with changed energy levels.