How to use and configure LARA

How to Set up and Control Your Model

This section describes setting up a model from scratch. If you like to develop your model using Repast Simphony, refer to RS Models. Of course, simulations need to be triggered somehow. Depending on

Using LAstractStandaloneSynchronisedModel

Implement createAgents() to create your agents and call addAgent(agent) for every created agent.

Fill the method getAgentIterable() that returns an Iterable over your agents.

Build your own subclass of LAbstractModel

Building your own realisation of LaraModel may be a suitable alternative when your model course deviates from the standard one.

  1. Design your model class that extends LAbstractModel
    1. Implement init() to setup your model, including the creation of agents.
    2. Implement onEvent(T event) and handle LModelStepEvent: let agents decide.

Important Hints

Agent Shuffling

When iterating through the agents for perception, pre-processing, decision etc. make sure that the order depends on the random seed, i.e. sort at the beginning (as long as the set of agents does not change) and shuffle at every time step using Your preferred random generator.

How to Design Your Agents

There are two different ways of implementing agent classes for LARA:

Custom class implementing LaraAgent

Implementing necessary methods
  • Constructor: Each agent instance needs to initialize and reference its LaraAgentComponent as the LARA specific agent component(usually an instance of LDefaultAgentComponent).
  • getLaraComp(): This method gives access to the reference to the LaraAgentComponent.
  • getAgentId(): Returns the (preferably) unique id of the agent.

    NOTE: It is important to assign a distinct ID to each agent since the ordering of behavioural options for instance depends on agents' hash code which - by default - is based on its ID. Furthermore, agents' equals() method is based on its ID.

Implementing optional methods
  • laraPerceive(): Here, the agent perceives environmental properties and might store these in his memory. Since for fetching the properties their names are required, the user needs to implement this process by his own.

Extending LAbstractAgent

The extending class should implement onEvent() and react to certain events:

@Override
public <T extends LaraEvent> void onEvent(T event) {
        if (event instanceof LAgentPerceptionEvent) {
                perceive(((LAgentPerceptionEvent) event).getDecisionConfiguration());
        } else if (event instanceof LAgentPreprocessEvent) {
                preProcess(((LAgentPreprocessEvent) event)
                                .getDecisionConfiguration());
        } else if (event instanceof LAgentDecideEvent) {
                decide(((LAgentDecideEvent) event).getDecisionConfiguration());
        } else if (event instanceof LAgentPostprocessEvent) {
                postProcess(((LAgentPostprocessEvent) event)
                                .getDecisionConfiguration());
        } else if (event instanceof LAgentExecutionEvent) {
                execute(((LAgentExecutionEvent) event).getDecisionConfiguration());
        }
}

Post_processing: Do evaluations of the decision or execution of behaviour in case of synchronised agent triggering here.

Of course, the agent instance needs to register itself at the event bus for the events it handles:

// subscribe to the events the agent should react on
eventBus.subscribe(this, LAgentPerceptionEvent.class);
eventBus.subscribe(this, LAgentPreprocessEvent.class);
eventBus.subscribe(this, LAgentDecideEvent.class);
eventBus.subscribe(this, LAgentPostprocessEvent.class);
eventBus.subscribe(this, LAgentExecutionEvent.class);

It is important to assign a distinct ID to each agent since the ordering of behavioural options for instance depends on agents' hash code which - by default - is based on its ID. Furthermore, agents' equals() method is based on its ID.

Decide on agent's memory

Properties that never shall be forgotten

While many properties have a natural retention time, i.e. they become forgotten in case they are not mentioned for a certain time span, others are kind of carved in memory. To model such everlasting memory items one might either...

  • ...add a memory that has no retention mechanism or
  • ...refresh these items regularly.

Configuring the Pre-Processor

Defining the order of pre-processor component activation

There are three ways to influence the order of pre-processor components:

  1. The order in which the decision mode selector publishes PP events.
  2. Definition of constraints on PP event level. See for instance LPpBoCollectorEvent.
  3. Definition of constraints on PP component level. This way the components asks the event bus whether required events have been processed or not.

These options differ in their flexibility and harshness. Whereas (1) requires only changes in the mode selector component, it is less binding since other components or events might define constraints that counteract the chosen order. Constrains on the level of components (3) are binding but require possibly many new implementations of other components and events to adapt to the new situation. Constrains on the level of events (2) are binding but require possibly many new implementations of other events and components to adapt to the new situation.

Setting Up the Pre-Processor at Agents

Even if the preprocessor is set to its default by LDefaultAgentComp in case it has not been set when it is required the first time, it's a good idea to think about the correct pre-processor configuration. There is also a reason regarding performance: Since the default procedure initialises a separate configuration for every agent it is recommended to use a global configuration before instead:

The pre-processing is configured using an instance of LPreprocessorConfigurator which then provides a LaraPreprocessor. See also How_to_Design_Your_Decision.

How to model the agents' environment

LARA provides means to associate each agent with its environment (or a hierarchy of environments). LARA does not directly depend on an agent's environment. Thus it is ok to assign null. However, in many cases the context plays a crucial role in assessing BOs or updating their preferences. Therefore, it is a good idea to keep the features in mind:

  • Assign listeners to an environment that are informed if properties change
  • Create a hierarchy of (potentially categorized) environments
  • Property management

When implementing the environment the first decision to make is whether one environment is enough or several environments (possibly as a hierarchy) are required.

One Environment is enough

In this case, your implementation just needs to extend LaraEnvironment. Of course, the default implementation (LEnvironment)does so but has additional functionality you may not need.

Requiring more than one environment

In this case, the LaraSuperEnvironment interface defines the methods one requires to register a bunch of LaraEnvironments at the base environment (the one that is known to the LaraAgentComponent). Since LEnvironment implements LaraSuperEnvironment one may use just this class as base environment (default) or let custom implementations extend it.

How to Design Your Decision

The actual selection of a BO is performed by an instance of LaraDecider which is selected in the pre-processor. Current decision mechanisms are:

Decision Modes

LARA currently provides these decision modes which are associated with an implementation of LaraDecider:

  • DELIBERATIVE (LDeliberativeDecider)
  • HABIT (LHabitDecider)
  • HEURISTICS_EXPLORATION (LExplorativeDecider; selecting a BO from memory at random)

    See Adding Decision Modes to introduce new decision modes.

Adapting Decision Mode Selection

There are several ways to adapt decision mode selection. Version A) is recommended when the decision mode strongly depends on agent properties and/or varies across agent types. Version B) is required if a custom LaraDecider shall be applied. Version B) might also be a good idea to speed up decision mode selection if it is actually very simple.

A) Delegating to Agent Class

Agents may implement the LaraDecisionModeProvidingAgent: the method getDecisionModeSuggestion() returns one of LaraDecisionModes (which is an enumeration). However, whether the return mode is considered depends on the applied LaraDecisionModeSelector. Furthermore, is may be required for the mode selector to choose another mode because the given one is not able to be applied.

B) Implementing LaraDecisionModeSelector

The default implementation of LaraDecisionModeSelector is LDefaultDecisionModeSelector. Of course, it is possible to extend this class in order to adapt decision mode selection, for instance for use of LTreeDecider. As LDefaultDecisionModeSelector provides protected methods to perform habit, deliberative, or explorative decision making it is pretty easy to implement the class. For most purposes, only onInternalEvent() needs to be overridden. The class de.cesr.lara.houseplatn.decision.DecisionModeSelector within the Houseplant model gives an example.

Depending on the selected decision mode the pre-processor needs to perform certain steps like collecting BOs and updating preferences. The LaraDecisionModeSelector is responsible to fire according event, e.g.:

protected void doDeliberative() {
        LaraDeciderFactory<A, BO> factory = LDeliberativeDeciderFactory.getFactory(agent.getClass());
        agent.getLaraComp().getDecisionData(dConfig).setDeciderFactory(factory);
        eBus.publish(new LPpBoCollectorEvent(agent, dConfig));
        eBus.publish(new LPpBoPreselectorEvent(agent, dConfig));
        eBus.publish(new LPpBoUtilityUpdaterEvent(agent, dConfig));
        eBus.publish(new LPpPreferenceUpdaterEvent(agent, dConfig));
}

Finally, the LPreprocessor needs to be configured for the newly implemented decision mode selector:

LaraPreprocessorConfigurator<AgentType, BehaviouralOption> ppConfigurator = 
        LPreprocessorConfigurator.<AgentType, BehaviouralOption> getNewPreprocessorConfigurator();

ppConfigurator.setDecisionModeSelector(new YourDecisionModeSelector<AgentType, BehaviouralOption>(),
                                yourDecisionConfiguration);
preprocessor = ppConfigurator.getPreprocessor();

The preprocessor then needs to be assigned to each agent's LARA component, of course:

getLaraComp().setPreprocessor(preprocessor);

Setting up Preferences

A preference value is valid for all decisions, and if the modeller wants to define a certain preference for several decisions with different values, he needs to define a preference for every decision.

Post-Processing

Post-Processing is intended to follow up the basic decision making before a selected BO is executed. This enables the agent to revise its selected decision, e.g. if the BO's performance is worse than desired. Furthermore, it is responsible to store the selected BO in the agent's general memory (this is the only action that LDefaultPostProcessorComp performs).

To implement custom post-processing, extend LDefaultPostProcessorComp and implement it's postProcess(A agent, LaraDecisionConfiguration dConfig) method. NOTE: Models that use the LDefaultDecisionModeSelector or a similar decision mode selector that enables habit decision mode need to call the super method to store selected BOs!

A LaraPostProcessor is assigned to the LaraAgentComponent for all decision. This is due to performance issues and practicable because special post-processing apart from the default storage of selected BO is rare. However, it is possible to implement a meat post-processor that references post-processors for certain decisions.

Using Events

Components are connected to the Eventbus following a publisher/subscriber model.

Events

Events and their properties
Event Publisher (typically) Subscriber (typically) Purpose State before State after
LModelInstantiatedEvent Your model controller Your models base class (AbstractLaraModel) Is fired on instantiation of the model, triggers initialization of your models base class. You just created a new instance of your model. Your models base class (AbstractLaraModel) initializes itself.
LInternalModelInitializedEvent LARA internal (AbstractLaraModel) Your model Is fired when the abstract model is initialized. The abstract model is initialized. Everythink is setup, so you can now initialize your model. Your model is initialized and ready to simulate the timesteps.
LModelStepEvent Your model controller Your model and your models base class (AbstractLaraModel) Is fired when a new timestep begins. Your model should trigger the model cycle (perceive » preprocess » decide » post » execute) now. The model has just been initialized and the model cycle begins OR the previous timestep just ended. A timestep has been simulated.
LUpdateEnvironmentEvent Your model Your environment Triggers the environment to update itself. Typically called when a new timestep begins. Your environment might be out-of-date. Your environment is now up-to-date. Environment is set up for the timestep.
LAgentPerceptionEvent Your model Your agents Triggers agents to perceive (every timestep). A new timestep begins, so there are potential changes in the environment. The agent has an up-to-date view of the current state of the environment.
LAgentPreprocessEvent Your model Your agent Triggers agents to preprocess. Agents perceived their environment. Agents are ready to decide about their actions.
LAgentDecideEvent Your model Your agent Triggers agents to decide (every timestep) Agents have perceived the environment for the current timestep, but did not make their decisions yet. Decisions are made, ready for action.
LAgentPostprocessEvent Your model Your agent Triggers agents to postprocess. Decisons were made by the agents. The agents actions are ready for execution.
LAgentExcecutionEvent Your model Your agent Triggers agents to execute (every timestep) - execute an action in LARA context typically means update the environment. The agents decisions where made. Agents changed the environment as a result of their actions. Next timestep can begin.
LPostExcecutionEvent Your model Your agent Used for internal components. You can hook in. Agent did execute. Timestep ends.
LModelFinishEvent Your model controller Your model Is fired when a simulation run is finished. The simulation has run and has now finished. File handlers are closed, all result data is stored etc. The program can now exit.
Communication with Eventbus

Subscribing

Subscribers must implement the interface LaraEventSubscriber. This requires the implementation of the method onEvent(T event). This method is called by the eventbus whenever an event of a type your components subscribes to is published. The code for the onEvent(T event) method in your component could look something like the following:

@Override
public void onEvent(T event) {
        if (event instanceof MyCustomEvent) {
                // do some work here
        }
}

To make subscribers listen to specific classes one needs to apply if-statements in the onEvent method and trigger warnings/error messages if other events occur. Finally, subscriptions works like this:

LaraEventbus eventbus = LModel.getLModel(id).getEventbus();
eventbus.subscribe(this, MyCustomEvent.class);
// your component will now be notified of events of type MyCustomEvent.class

Publishing

Assuming you would like to have your class MyCustomPublisher publish an event of type MyCustomEvent.class you would put something similar to the following code in the code of your component.

LaraEventbus eventbus = LaraEventbus.getInstance();
// create an instance of an event an publish it
even    tbus.publish(new MyCustomEvent());
// all subscribers of events of type MyCustomEvent.class will now be notified

Execution of Preceding Events required

Even that depend on the former execution of another event should implement LaraRequiresPrecedingEvent. The eventbus then checks whether the event returned by getRequiredPrecedingEventClass() has been fired. Otherwise, it issues a WARN message and does not trigger the event.

Consecutive Events

Events are marked having consecutive events by implementing the interface LaraHasConsecutiveEvent and the method getConsecutiveEvent(). This means, after the event is performed the eventbus fires the consecutive evnt automatically.

Forcing LEventbus to work sequentially

There is an option to force LEventbus to treat event sequentially no matter how they are defined:

PmParameterManager.setParameter(de.cesr.lara.components.param.LBasicPa.EVENTBUS_FORCE_SEQUENTIAL, true);

NOTE: Make sure to set the parameter before the LEventbus is initialised!

Implementing Custom Events

Whenever possible, event classes should be implemented as singletons (only private constructor + getInstance())

There are different classes of events to realise that cause a different execution behaviour:

Event class Execution
LaraSynchronousEvent Synchronous (Parallel execution, Eventbus waits until all subscribers finish)
LaraAsynchronousEvent Asynchronous (Parallel execution, Eventbus does not wait)
LaraSequentialEvent Sequential (sequential execution)

Ticks and Dates

LModel.getModel().getCurrentDate();

Applying Decision Trees

Scope of Use

Decision trees are suitable to implement rule-based heuristics for decision making. In this case, the result objects are BOs. Furhermore, they can be used to determine the decision mode. Result objects are then likely to be of type LaraDecisionModes.

LARA currently provides abstract classes for binary trees. Each inner part of the tree has an evaluate() method that decides which subtree to follow. Leaves have a getSelectedBos() method.

Setting up a Decision Tree

Desicion trees always return a set of result objects instead of single result object to be useable also as preselector in the pre-processor step. It is convenient to use LSet to easily initialize the required set.

LaraDeciderFactory<Agent, AbstractIrrigationBehaviouralOption<Agent>> factory = 
        new LTreeDeciderFactory<Agent, AbstractIrrigationBehaviouralOption<Agent>>(new DecisionTree());
agent.getLaraComp().getDecisionData(dConfig).setDeciderFactory(factory);

An example for the use of a decision tree is in the houseplant model, classes DecisionModeSelector and DecisionTree. Another compact example can be found in the test class LTreeDeciderTest.

Issues that need to be considered for certain kinds of applications

You do Batch Runs, especially in Repast Simphony

When you do several runs in line, do not forget to clear the agent collection. Especially in Repast Simphony, the class that implements ContextBuilder stays alive, and agents in collections that are properties of this class may survive as well. Since the LAbstractAgent implements equals() and hashCode() according to the agent ID, this might cause a lot of trouble.

Furthermore, you need to reset LARA:

LModel.getModel().resetLara()

Your model consists of more than one agent class

To simplify accessing agent methods we use java generics. However, there is an obstacle using generics in combination with the Singleton Design Pattern that ensures a limited amount of instances existing. To preserve efficiency, consider the javadoc of LDeliberativeDeciderFactory, for instance.

Analysing LARA's Behaviour

LARA uses Log4j (API) for logging all kind of information. It provides extensive support to get the messages you want in a way you need.

Setting up the Logging Framework

Configuring Log4j is quite comfortable by using name/value-pairs in configuration files. These file are located in the "config/log4j" folder. The files mostly contain several options to choose from. Symbols for the pattern layout are found here. To apply a configuration file, add

-Dlog4j.configuration=confFilename

as VM argument in the Run Configuration.

Tools to Analyse Loggings

To analyze logs in eclipse, Ganymed is a valuable tool. Get it by adding

http://ganymede.sf.net/update

as software site in eclipse. It requires the SocketAppender to receive logs from Log4j.

Another famous standalone-tools for analyzing log event is Chainsaw, that should work well with log files produced by Log4j's XMLLayout Appender.

Inspect applied Decision Modes

Basically, the decision mode is represented by the applied decider and can be accessed as follows:

agent.getLaraComp().getDecisionData(decisionConfig).getDecider().getDecisionMode()

Note that the decider is no longer available when an LAgentPostExecutionEvent was triggered that removes the according decision data.