Since version 2.0.0, Alchemist uses YAML as primary mean for writing simulations. Historically, it relied on XML instead, but the system has since been deprecated, due to its verbosity and human-unfriendliness. Some resources on how to write a proper Alchemist XML are still available here. Keep in mind, however, that such method is now deprecated and unmaintained, and it is likely to get dropped if any major breakage occurs. If you are new to Alchemist, we recommend to follow the remainder of this page, and learn about the wonders of YAML.
As a first step, we recommend learning the YAML basics. The language is so simple and human readable that there is probably no better way to learn it than to read it directly. My suggestion is to use the tutorial “Learn X in Y minutes where X = YAML”, it should provide a good YAML guide (surely sufficient to follow the tutorial).
The Alchemist YAML
Alchemist expects a YAML map as input. In the following section, we’ll discuss which keys it expects. Of course, users are free to use all the YAML features (e.g. anchors) to organize their code and reduce duplication.
Class loading with the Alchemist YAML
One important aspect of the Alchemist YAML is the ability to let the user control which actual Java classes should be loaded inside a simulation, and which constructor should be used to do so. Almost every entity of an Alchemist simulation can be instanced using arbitrary Java classes that implement the required interfaces. When the alchemist YAML parser encounters a YAML Map providing the keys
parameters, it tries to resolve the value of the value associated to
type to a class name, then tries to create the object by calling the constructor with parameters most suited to the value of
Class name resolution
The value associated with
type must be a string representing a valid Java identifier. If the value contains one or more
. characters, then it will be interpreted as a fully qualified name. If no such character is included, then the default package for the desired alchemist entity will be prefixed. Alchemist won’t ever attempt to load a class situated in the default package (but I am sure good people like you don’t put stuff there, do you?).
If the class gets loaded correctly (meaning if a class is present in the classpath with the fully qualified name, whether it was passed or guessed by Alchemist), then its constructors get sorted based on the number and type of parameters. The system tries to build an object with all each constructor until one of them provides an instanced object, in an order that considers both the current context (namely, the entities that have already been instanced) and the value of
For instance, imagine that you are trying to build an instance of a Reaction, whose only constructor requires an Environment, a Node, an
int and a
String. In this case, an Environment and a Node must have already been created (or the YAML loader won’t be at this point). As a consequence, the first two parameters are automatically inferred by the current context and passed to the constructor. The other two parameters can not be inferred this way; instead, the value associated to
parameters is used to extract the proper values (if possible). In this case, this would have been a valid
As you can easily infer, the value of
parameters must be a YAML list.
Don’t despair if the class loading system is still unclear: it is used pervasively and it will become clearer with the examples in the next sections.
incarnation key is mandatory. The YAML parser expects a string value, and does not support the class loading mechanism. Such string will be used to get the most similarly named incarnation (the algorithm may vary), namely the subclass of Incarnation whose simple name is closest to the string. The (obvious) suggestion is to use an existing incarnation name, such as
protelis. New incarnations may (and will) be available in future.
Note: this is also the most minimal valid alchemist specification
variables section lists variable simulation values. A custom variable is defined in a Java class which implements the Variable interface. If no fully qualified variable name is provided for class loading, Alchemist uses the package variables to search for the class.
seeds section may contains two optional values:
simulation. The former is the seed of the pseudo-random generator used during the creation of the simulation. For instance, perturbating grid nodes in the
displacement section. The latter is the seed of the pseudo-random generator used during the simulation. For instance, handling events concurrently (which event occurs before another).
Setting seeds with integer values.
Setting seeds with variables.
environment key is used to load the Environment implementation. It is optional and it defaults to a continuous bidimensional space. If no fully qualified environment name is provided for class loading, Alchemist uses the package environments to search for the class.
The following simulations are equivalent, and load the default environment (which is incarnation independent, here
protelis is picked, but it works for any other incarnation as well):
The following simulation loads data from an Openstreetmap file (OSM, XML and PBF formats are supported) located in the classpath in the folder
The following simulation loads data from a black and white raster image file located in the classpath in the folder
images , interpreting the black pixels as obstacles (areas that cannot be accessed by nodes):
The following simulation loads a personalized class named
my.package.FooEnv implementing Environment, whose constructor requires a String and a double:
More about the environments shipped with the distribution here.
positions section lists the coordinate types of the simulation. Actually, only one value is taken into account. Each type implements the interface Position. If no fully qualified position name is provided for class loading, Alchemist uses the package positions to search for the class.
position type should reflect the simulation’s physical features. For instance, when a city map is considered,
Continuous2DEuclidean distance might be no longer suitable. Given two points A and B,
distance(A, B) may differ from
network-model key is used to load the implementation of linking rule to be used in the simulation. It relies on the class loading mechanism, it is optional and defaults to NoLinks (nodes in the environment don’t get connected). Omitting such key is equivalent to writing any of the following:
If no fully qualified linking rule name is provided for class loading, Alchemist uses the package linkingrules to search for the class.
displacements section lists the node locations at the beginning of the simulation. Each displacement type extends the interface Displacement. If no fully qualified displacement name is provided for class loading, Alchemist uses the package displacements to search for the class.
A single point located in (0, 0).
10000 nodes, placed in a circle with center in (0, 0) and radius 10.
Nodes are randomly located in a square with a 0.1 distance units long side, centered in the point where the node was previously placed.
It is possible to set the content of the nodes inside a given region. Only the nodes inside the
Rectangle area contain the
randomSensor molecules (global variables).
Nodes can execute a list of protelis programs.
export section lists which simulation values are exported into the
folder specified with the
-e path/to/folder argument. Data aggregators are statistically univariate. Valid aggregating functions extend AbstractStorelessUnivariateStatistic.
Extending the simulation
It is possible to enrich the simulation with custom classes (variable types, displacements, etc). The latter have to be included in the simulation classpath and have to implement/extend the respective interfaces/abstract classes.