MAML Tutorial
Introduction
This document was created to give a quick introduction to MAML.
It does not contain the whole functionality of the language, but explains
its basic structures and most of its capabilities. Note that some
elementary knowledge of Objective-C (and thus C) is required, as only
the new features of MAML are explained. Some knowledge of Swarm
can also be helpful. (If you are interested, you can find references
to the documentation of different versions of Swarm at the Swarm
Home Page.)
The tutorial consists of a few small models, ranging from very elementary
models to small, but already interesting, scientific ones. A
short description of the succeeding model and a summary of the newly introduced
language elements precede each step. Every model is commented upon, but
because only the new features are explained, following the order in which
the steps are presented may avoid confusion.
The source code of the programs is available in two versions: one
suitable for Swarm 1.3 (this version
will be used throughout this document) and the other suitable
for Swarm 1.0.2. There is only one exception: if you're using
Swarm 1.4.1 or 2.1.1, you'd better get the source for steps 4.1
and 4.2 from here or here.
[Top]
Step 0 -- The minimal model
This is the simplest model you can create in MAML (m0.maml).
It merely defines a closed system (a model). Just
take a look at it as a reference point, and continue your tutorial
with the next step, in which we'll give you another simple, but more
interesting model.
[Top]
Step 1 -- A really dumb model
This model is one of the simplest agent-based models you can create. It
contains two agents ('dumb', and 'dumber') of the class Dumb. After their
creation, these agents 'subscribe' to the model
(that is they produce some minimal 'sign of life').
In this step we introduce the most important MAML elements, which are
the following:
| @model |
Starts a model. The model goes between curly braces. |
| @agent |
Starts the description of an agent. |
| @var |
Declares variables. |
| @sub |
Declares a subroutine (subprogram). |
| @init |
Denotes the beginning of the initialization code of the model (which
is executed first). |
| @create |
Creates agents. |
Here you can find the source code of this model
(m1.maml). Let's compile it with the xmc
compiler (for downloading information and usage guide refer to the
XMC Download Page)!
By trying out the model you will find that it displays the common control
panel of swarm simulation, but produces no other visible results. This
is logical because we've just created a model (a system), and nothing
else. To receive the results of its activities, we need to observe it by
the means of an observational tool that 'crosses the boundary' of the closed
system. For this we must extend our model, which is done in the following
substeps that demonstrate what MAML provides in order to complete this
task.
[Top]
Step 1.1 -- Graphical observation of the first model
The model/observe pair presented here provides a simple graphical observation
of the behavior in model 1. In this substep, the model itself hasn't changed,
but an extension (a so called 'observe' section) has been added. This observe
section describes what the interface of the model (which is a 'closed world')
is to the user running the simulation. The interaction can be bidirectional.
The parameters of the model can be set by the user: this is an 'input'
to the simulation. The output of the simulation can be visualized or saved
into files.
One way to monitor and modify the value of variables is to "probe"
them: displaying them in a window on the screen. (With probing even subroutine
calls can be inserted run time.) For the model (and for each agent class)
we can set which variables (and subroutines) should be probed.
| @observe as |
Starts the observe section. The structure must include the name of
the model being observed (the model with that name must be provided). The
observe section also contains a section for initialization. |
| @probe |
Sets the probed variables (and subroutines) for the model (or an agent
class, depending where the @probe is). |
| @buildProbes |
To be called before invoking [self probe] (or [anAgent probe] for an
agent instance), which actually displays the probe window for the model
(or anAgent). (It is possible that the @buildProbes keyword will be either
removed or given additional functionality.) |
source
code m1.1.maml
Besides the control panel there is a "probe window" titled "m1" containing
the probed variables of the model (in this case only one). The value corresponding
to this variable is 0 at the beginning and becomes 2 after the 0th timestep.
[Top]
Step 1.2 -- Batch mode observation of the first model
More than one observe section can be written for a specific model. Here
we present another observation of model 1. This observation is not graphics-based:
it simply writes output data into a file. This technique is
preferred when one runs a simulation off-line, "in batch mode". This
is usually the case when the "parameter space" is investigated: that is,
when multiple copies of the same simulation with different (initial) parameters
are executed, often at the same time.
| @observe batch |
The "batch" modifier indicates that there is no need for a graphical
interface (controlPanel). |
| @initModel |
Triggers the execution of the initialization section of the model.
With this you can rule when the initialization of the model should happen.
In the observe init you can execute a piece of code before it (this is
the default if there is no @initModel) and another piece after it. |
| file-handling (C-language) |
For details of file handling in C language, please refer to your C
Language Book. |
source
code m1.2.maml
If the execution of the simulation takes place in a user environment
without graphical user interface (namely X Windows), the simulation should
be started with the run -batchmode command instead of the simple
run command. The absence of a graphical user interface results
in the error: TkExtra (instance)run-time error message.
When executed, the output of the simulation (the value of numOfSubscribedAgents,
which is 2) is written to the file named OUTPUT.
[Top]
Step 2 -- A very simple counting 'model'
In this example the model contains one single instance of the agent class
Counter. This agent counts how many times its "increase" subroutine was
called. During the simulation the invocation of this subroutine is triggered
in each timestep. The simulation lasts for 100 timesteps.
| @schedule cyclic (1) |
A schedule is an abstraction of a mechanism that can trigger certain
events at certain timesteps. In this form the body of the construct (0:
@to counter increase;), namely the invocation of the counter agent's increase
subroutine, will be triggered in each timestep. The cyclic modifier means
that the body should be executed not only once, but it should be restarted
after a certain number of timesteps (here "1", in general "n"). The structure
of this n timesteps is described in the body. For example, in the 0th timestep
of each n length cycle the [counter increase] invocation takes place. (In
this case this is the only timestep, as the length of the cycle equals
1.) |
| @to |
Used to set the agent that should receive a message. |
| @create{} |
When an agent is created, it can also be initialized. |
| @observe gui |
Here "gui" means that the observation is with graphical user interface
(control panel, etc.). Gui is the default. |
| @extendAgent |
Adds new features to an agent class in the observation. (Note, this
is not inheritance!) Here we define a probe to the agent class Counter.
The probe window for the counter agent (the instance) will be displayed
during the initialization of the observe section but after the initialization
of the model (when the counter agent has already been created). |
| maxTimeSteps |
The number of timesteps the simulation runs. If this variable is not
set (that is, it equals -1) there is no restriction on the length of the
simulation. Changing the value of maxTimeSteps after the initialization
of the model and that of the observe section has no effect. After reaching
maxTimeSteps timesteps, the simulation terminates (and quits). The advantage
of maxTimeSteps can be best exploited in batch mode. |
source
code m2.maml
When running the simulation in the counter probe window we can follow
how the count value increases in each timestep.
[Top]
Step 3 -- Race
In this model we have 10 agents of class Competitor. These agents jump
ahead in each timestep with a random value (from 1 to 3), increasing the
value of their "distance" attribute). In each timestep we monitor the currently
leading agent in the observe section. The agents are identified by their
"row" attribute.
| $define |
Assigns a (global) name to a certain value using this keyword. All
occurance of the name in the source code will be replaced with the assigned
value. The mechanism is similar to the #define in C: this is a macro definition
expanded by the preprocessor (the first phase of the compiler). |
| array declaration |
In a @var construct (or in a parameter list of a subprogram) the []
signifies the declaration of an array.
In this example we declare an array of Competitor agents. |
| array creation (using index in initialization) |
Using the @create construct one can create and initialize an array
of agents. In the initialization part the array index can also be referred. |
| @forEach |
This keyword, in a @schedule construct, indicates that the appropriate
message should be sent to more agents at the same time. The argument of
@forEach should be a collection of agents. |
| groupOf |
For each agent class a predefined collection variable is assigned:
for agent class A the variable groupOfA. This collection variable contains
all agent instances of the corresponding class. |
| no name needed for observe |
The observe construct doesn't have to have an "as" clause. The observe
doesn't have to have a separate name. |
| uniformIntRand (Swarm) |
For (pseudo)random number generation you can use the appropriate Swarm
libraries. |
| inserting C code |
Standard C (Objective C or Swarm) code can be written in certain parts
of the MAML code, for example in a subroutine body or in a @init part.
See the body of subroutine searchForFront in the observe section. |
| Probing variables introduced in the observe section. |
Whenever you introduce a variable by the @var clause, the variable
can be probed. Note that the variables 'row' and 'distance', (introduced
in the observe section to monitor the currently leading agent) are also
probed. |
source code
m3.maml
During each timestep, the observe section receives a searchForFront
message that finds out which agent has the largest distance attribute.
This value and the row attribute of the "winner" agent is shown in the
probe window of the model. In another probe window, the attributes of the
first competitor (with row number 0) are displayed.
Exercise: Modify both the model m3 and the observe section by removing
the row attribute from the Competitor agent class. Use the index of agents
in the competitors array to identify the agents.
[Top]
Step 3.1 -- A variant of 3
The searchForFront subroutine of the observe section can be replaced with
a new subroutine in the extension of the Competitor agent class (which
is invoked in every timestep). Thus, each step of the loop statement in
searchForFront is replaced with the execution of the appropriate Competitor
agent's "subscribe" subroutine. The subscribe subroutine sends the agent's
row and distance to the observe section through the invocation of the newJumpOf:To:
subroutine. A new schedule is introduced that will trigger the subscribe
subroutine of each Competitor agent in each timestep.
source
code m3.1.maml
Step 3.1.1 -- A variant of 3.1
In 3.1 the Competitor agents send the subscribe message to "the observe
section". In 3.1.1 a new agent is introduced for this purpose. This agent,
an instance of FrontLiner, is defined in the observe section. This illustrates
that not only a model, but also an observation can be written in an "agent-based"
manner.
source
code m3.1.1.maml
(Another variant of 3.1.1 is being developed...)
Step 3.1.2 -- A variant of 3.1
This model is almost the same as 3.1, but instead of introducing a new
schedule to trigger the subscribe subroutine calls, we extend an existing
schedule. The schedule that will be extended should be supplied with a
name.
source
code m3.1.2.maml
[Top]
Step 4 -- Party Preferences
Civilians change their preferred party over time as parties change what
they offer.
In this model there are two types of agents: parties and civilians.
Each civilian belongs to one of the parties. In each timestep they reconsider
which party is the best for them. The decision is based on what the parties
offer to their members. The basic rules are the following:
-
There are a number (numOfOffers) of aspects to be considered, represented
as integer values in the range 0..maxOffer. These values are different
for each party (stored in the party's offer attribute).
-
Each civilian has a weight to describe to what extent an aspect i is important
to him. These weights are integer values in the range 0..maxWeight, stored
by each civilian in the weights attribute.
-
The overall advantage a party can supply is the basis of the decision:
in each timestep the agent chooses the "best" party for him. This overall
advantage is the weighted sum of the offers of that party.
-
In each timestep the offers of a party are modified with a random number
in the range of -changeInOffer..changeInOffer. (This modification represents
how the goals of the party in that aspect came true.)
-
The offer is also modified according to the current size of the party.
The number of the party's members is divided by membersQuotient. This result
is added to the offer.
-
The civilians are loyal to their formal parties. The overall advantage
of a civilian's current party is increased by the product of the number
of timesteps during which the civilian is in that party (fidelity attribute)
and the fidelityFactor.
The parameters of the model are: the number of parties and civilians (numOfParties
and numOfCivilians, respectively), numOfOffers, maxOffer, changeInOffer, maxWeight,
membersQuotient and fidelityFactor.
The goal of the model is to examine the size of the parties and their
changes over time. We expect that the size of the leading parties will
be stable after a while.
Initially the offers of the parties are set equally and the weights
of the offers in the civilians are set randomly. The initial distribution
of the civilians among the parties is also randomly chosen. During each
timestep, the parties calculate their modified offers and then the civilians
make their decision about their favourite parties.
| declaration-creation-initialization of (int) array variables |
The declaration, creation and initialization of arrays of values is
analogous to those of arrays of agents. There are arrays of int values
presented in this model. We can use [] in @var constructs and subroutine
parameter lists to declare arrays. We can use the @create construct to
create arrays (of dynamic size), and even give an initialization part as
the body of the @create. |
| @planDef |
This keyword defines a plan. Such a definition contains a number of
actions to be performed in a certain order: sequentially (seq: this is
the default, and currently the only supported), concurrently (con), or
in random order (rnd). The actions are subroutine invocations with a certain
target, e.g., an agent or a collection of agents. |
| @plan |
This keyword activates a plan. Such an activation can take place in
an @schedule structure. The plan is executed in a certain timestep; in
other words, the plan's actions are executed in the specified order. |
| parametrization |
The following demonstrates an easy way to parametrize a simulation.
The parameters are variables in the model. They are initialized at the
beginning of the @init part of the observe section. Then a probe window
for the model containing the parameter variables is created and displayed.
The user now is able to reset the parameters. After that, he/she starts
the simulation by pressing Go or Time Step on the control panel. At this
point the initialization of the model (according to the parameters) takes
place: @initModel. At the end of the init part of the observe section,
probes on certain agents of the model can be added, as well as other observational
tools (see the next examples). |
| [controlPanel setStateStopped] |
This message call stops the simulation in a GUI observer. The simulation
can later be reactivated by the user, hitting the 'Go' or 'Timestep' button
on the program's graphical interface. This line is used when we stop the
simulation to allow the user to alter the model parameters, after the probes
with the default values have been displayed. |
When you start the program, first the control panel and the probe display
of the model appear. At this point you can reset the parameters of the
simulation run. Pressing the Time Step button on the control panel results
in starting the simulation: the probe windows for all parties are displayed
containing the size of the appropriate party.
source
code m4.maml
[Top]
Step 4.1 -- A more sophisticated graphical observer for 4
Instead of displaying the size of the parties in probe windows this application
draws a histogram in each timestep. Each coloumn in the histogram corresponds
to the size of a party.
Because
the probing mechanism is very time consuming, replacing it with the histogram
results in that the speed of the simulation is increased.
| @uses |
This keyword is used to make library units (like <stdio.h> or "my_definitions.h")
visible. In this example <analysis.h> is made visible. (The @uses mechanism
is like importing/including header files in Objective C or C to make
the definition of types and subprograms visible.) |
| analysis.h |
One of the library units in the Swarm package. It contains (among others)
the EZBin protocoll. (EZBin class in Swarm 1.0.2) |
| EZBin |
A protocol in Swarm. (It is a class in Swarm 1.0.2) It is used to display
histograms. See the source, regarding the steps to initialize and update
the histogram. |
| @planDef in @schedule |
One way to use @planDef is inside a @schedule. The result is similar
to what we saw in Step 4. |
source code
for
2.1.1,
for
1.4.1 and
for 1.3:
m4.1.maml
[Top]
Step 4.2 -- Another graphical observer based on 4.1
We introduce an additional graphical observer tool to 4.1. Using a graph,
we record how many civilians have changed parties in each timestep. (Notice
that this is not equal to the changes in the sizes of the parties.)
| EZGraph |
Another protocol in Swarm, again from <analysis.h>. It is used to
display graphs. See the source regarding the steps to initialize and update
a graph. (Again, it is a class in Swarm 1.0.2) |
| @extendPlan |
Keyword to add new actions to the plan, thus extending a plan definition
in the observe section. |
source code
for
2.1.1,
for
1.4.1 and
for 1.3:
m4.2.maml
[Top]
Step 5 -- Thomas Schelling's famous Segregation Model.
This simulation is based on a model published by T. Schelling, and consists
of agents living in a 2d grid. The agents are either blue or red, and remain
at a given location only if a specified limit is less than the ratio of
the number of their neighbors having the same color and the total number
of neighbors. It is interesting if this limit is fairly low (appr. 0.5),
because this fairly tolerant behaviour leads to surprisingly high rate
of segregation on the global level.
| $import |
This keyword includes the specified file in the MAML source code at
the location where it appears. This keyword's main use allows for separating
the model into distinct files, enhancing the model's readibility.
If the file to be included was already imported by a previous use of
this keyword, then the keyword is ignored. (That is, you can only import
a file once in a model, although it is not reported as an error.) |
| boolean |
A MAML type for truth values. It has two legal values: true and false. |
| @schedule cyclic (displayFrequency) |
Note that in the observer we use a variable (namely the 'displayFrequency'
variable) to specify the length of the schedule cycle. |
| nil (Objective-C) |
Constant that denotes the non-existing agent. (e.g., in the case of
a grid holding agents, you find this constant at an empty location. |
| M(drawSelfOn:) (Swarm) |
The way to specify a message in Swarm, and thus in MAML. Usually
this is used when specifying which message should be sent by certain tools,
such as the Object2dDisplay tool (described below). The format is the following:
M(message_name)
Note the colon at the end of the message name in this example. This is
to specify that this message has one argument to pass with. |
| The following features are tools
coming from Swarm: |
|
| space.h |
A Swarm library to handle 2d grids of different kind. |
| Discrete2d |
One of the grids from the space.h library. It can hold objects (agents)
on a 2d grid with discrete coordinates. |
| collections.h |
Another Swarm library which provides several data structures that store
collections of agents. |
| List |
One of the simplest collections from the collections.h library. It
stores a list of agents. You can dynamically extend or reduce the list. |
| analysis.h |
A Swarm library we've already used during the course of this tutorial.
(Check out step 4.1!) |
| ZoomRaster |
A tool from the library analysis.h, to display a zoomable window containing
graphics. (We use it now to show the 'Segregation World' inside it.) |
| Object2dDisplay |
A tool to display a collection of agents on a graphics window. A specified
method is called on each member of the collection: that method draws the
mark corresponding to the given agent onto the window specified. |
| XColormap (Swarm 1.0.2) or Colormap (Swarm 1.3) |
An object holding a color palette (or scale) that can be used when
drawing. |
First the control panel pops up and the probe display of the model:
here the user can reset the parameters of the simulation. Pressing the
Time Step or the Go button on the control panel results in starting the
simulation: the display of the world is displayed under continuous change
as the simulation runs.
This is the first model which consists of more than one files.
Please, check them all.
main
source file (the observer which takes the model) Segregation.maml
the
model's file (which takes the agents) m5.maml
the
agents' source resident.maml.stub
[Top]
|