krABMaga is a discrete events simulation engine for developing ABM simulation written in the Rust language.

krABMaga is designed to be a ready-to-use tool for the ABM community and for this reason the architectural concepts of the well-adopted MASON library were re-engineered to exploit the Rust peculiarities and programming model, in particular by keeping the visualization and the simulation subsystems fully separated.

Developed by IsisLab ISISLab Logo


Table of contents§


Dependencies§

The visualization framework requires certain dependencies to run the simulation properly.

  • 💻 Windows: VS2019 build tools
  • 🍎 MacOS: No dependencies needed.
  • 🐧 Linux: A few dependencies are needed. Check here for a list based on your distribution.

How to run your first example simulaton§

First of all, install latest version of Rust. Follow steps to setup Rust toolchain (cargo, rustc and rustup).

Now, you can download/clone all available krABMaga examples from our github repository called examples.

To run a simulation, go to root directory of a model, for example /path/to/examples/flockers. With command ls, you should be able to see a typcal krABMaga simulation struct:

  • src: main folder with code. It contains main.rs file and two directories for model and visulization components.
  • Cargo.toml: Configuration file for Rust project, with dependencies and features.
  • assets: an images folder. It contains all the icons that can be used for visualization.
  • Makefile.toml: another configuration file, necessary to a correct execution of visualization.

Inside the root directory of model that you choose, you can run a models with or without visualization.

To simply run your simulation, with no visualization:

cargo run --release

Running in this way, you can see our Simulation Terminal (better known as Simulation Monitor) based on tui-rs, a rust library that provides components to create terminal with an interface. As a modelist, you can use krABMaga macros to create several plots, print logs and add a model description (shown using a popup)

Based on Bevy game engine, it's possible to run simulation with visualization. It's also available a menu to start and stop simulations and a slider to set simulation speed. To run a model with visualization enabled, you have to start the simulation with the command:

cargo run --release --features  visualization

# Alternative command. Requires 'cargo make' installed
cargo make run --release 

In addition to the classical visualization, you can run your krABMaga simulation inside your browser using Web Assembly. This is possible with the command:

# Requires 'cargo make' installed
cargo make serve --release 

How to write your first model§

If you don't start from our Template, add this to your Cargo.toml:

[dependencies]
krABMaga = { git="https://github.com/krABMaga/krABMaga.git" }

[features]
visualization = ["krABMaga/visualization"]
visualization_wasm = ["krABMaga/visualization_wasm"]

We strongly recommend to use Template or any other example as base of a new project, especially if you want to provide any visualization.

Each krABMaga model needs structs that implements our Traits, one for State and the other for Agent. In the State struct you have to put Agent field(s), because it represents the ecosystem of a simulation. More details for each krABMaga componenet are in the Architecture section.

The simplest part is main.rs, because is similar for each example. You can define two main functions using cfg directive, that can remove code based on which features are (not) enabled.
Without visualization, you have only to use simulate! to run simulation, passing a state, step number and how may time repeat your simulation. With visualization, you have to set graphical settings (like dimension or background) and call start method.

// Main used when only the simulation should run, without any visualization.
#[cfg(not(any(feature = "visualization", feature = "visualization_wasm")))]
fn main() {
  let dim = (200., 200.);
  let state = Flocker::new(dim, num_agents);
  let step = 10;
  let reps = 1;
  let num_agents = 100;  
  let _ = simulate!(state, step, reps);
}

// Main used when a visualization feature is applied.
#[cfg(any(feature = "visualization", feature = "visualization_wasm"))]
fn main() {
  let dim = (200., 200.);
  let num_agents = 100;
  let state = Flocker::new(dim, num_agents);
  Visualization::default()
      .with_window_dimensions(1000., 700.)
      .with_simulation_dimensions(dim.0 as f32, dim.1 as f32)
      .with_background_color(Color::rgb(0., 0., 0.))
      .with_name("Flockers")
      .start::<VisState, Flocker>(VisState, state);
}


Available features§

This library offers some features to make your simulation more interesting and to avoid to install many dependencies that are not needed for basic simulation.

cargo run --release --features <name_feature>
Compilation FeatureDescriptionExperimentalRelease CandidateStable
No FeaturesPossibility to run model using Simulation Terminal and setup model-exploration experiments (Parameter Sweeping, Genetic and Random) in sequential/parallel mode. It's enough to create your base simulations.🦀
visualizationBased on Bevy engine, it makes possible to visualize your model elements, to understand better the behavior of your simulation.🦀
visualization-wasmBased on Web Assembly, give you the possibility to execute your visualized simulation inside your own browser.🦀
distributed-mpiEnable distributed model exploration using MPI. At each iteration, the amount of configurations are balanced among your nodes.🦀
bayesianUse ML Rust libraries to use/create function to use Bayesian Optimization.🦀
parallelSpeed-up a single simulation parallelizing agent scheduling during a step.🦀

Macros for playing with Simulation Terminal§

Simulation Terminal is enabled by default using macro simulate!, so can be used passing a state, step number and how may time repeat your simulation.. That macro has a fourth optional parameter, a boolean. When false is passed, Simulation Terminal is disabled.

($s:expr, $step:expr, $reps:expr $(, $flag:expr)?) => {{
      // Macro code 
}}

You can create tabs and plot your data using two macro:

  • addplot! let you create a new plot that will be displayed in its own tab.
addplot!(String::from("Chart Name"), String::from("xxxx"), String::from("yyyyy"));
  • plot! to add a point to a plot. Points can be added during simulation execution, for example inside after_step method. You have to pass plot name, series name, x value and y value. Coordinate values need to be f64.
plot!(String::from("Chart name"), String::from("s1"), x, y);

On Terminal home page there is also a log section, you can plot log messages when some event needs to be noticed. You can navigate among all logs using ↑↓ arrows. To add a log use the macro log!, passing a LogType (an enum) and the log message.

 log!(LogType::Info, String::from("Log Message"));

Are available four type of Logs:

pub enum LogType {
    Info,
    Warning,
    Error,
    Critical,
}

How to contribute§

If you want to test, add or change something inside krABMaga engine, you can clone main repo locally, and change dependecy inside Cargo.toml of your examples:

[dependencies]
# krABMaga = { git="https://github.com/krABMaga/krABMaga.git" }
krABMaga = { path="path/to/krABMaga"}

Architecture§

krABMaga Architecture

Agents§

The krABMaga framework defines a trait Agent that can be implemented on a struct to define Agent specific functionalities, mainly the step method which specifies how the agent behaves for each simulation step, and the get_id method, to uniquely identify an agent. There are also other methods, with default implementation, to improve agent control:

  • is_stopped notify the scheduler if a specific agent should be removed or not, based on some condition.
  • before_step and after_step to implement some operations before/after a step.

The krABMaga framework allow multi-agent implementations: you can define multiple 'Agent' that implement the trait, and Wolf, Sheep & Grass is the main example of this feature.


Simulation state§

The simulation state can be considered as the single source of truth of the simulation, where data resides and is updated. Like Agent, krABMaga exposes a State trait to let the user mark a particular structure as a simulation state, along with exposing an update method to define logic to execute once for each simulation step. The simulation state is the perfect structure to put field definitions on (such as 2D continuous fields, grids and so on). An important effect of the state being the single source of truth forces agents to update (and most importantly read) their own location by interacting with the state, even though they can store their own location locally in the agent structure too. Although, to be sure one is interacting with the latest computed data, it is considered a good practice to update both an agent own location field and its copy on the state structure.


Schedule§

The simulation timeline is controlled by a Schedule structure that takes care of notifying all the scheduled agents, and the simulation state that a step has been taken. For this reason, agents should be scheduled so that they can be notified when a step has been taken. The scheduler works as a priority queue, where the agents are sorted according to their scheduled time and a priority value - an integer. The simulation time - a real value - starts from the scheduling time of the first agent. The schedule structure exposed by the krABMaga framework provides two methods to do so:

  • schedule_once to insert an agent in the schedule for a specific simulation step. The scheduling time and the priority are given as parameters. The priority is used to sort all agents within the same simulation time.

  • schedule_repeating which acts like schedule once, with the difference that the agent will be scheduled for all subsequent simulation steps.

The schedule provides the step method which allows executing one simulation step. In this way, the programmer can easily design his/her simulation by looping for a certain number of step or for a given amount of CPU time.


Data structures§

The currently implemented structures are:

  • Field2D, a sparse matrix structure modelling agent interactions on a 2D real space with coordinates represented by 2D f32 tuples (Real2D).

  • Grid2D, a discrete field representing agents locations as 2D i32 tuples (Int2D). This structure keeps two copies of a DBDashMap in sync, one the inverse of the other, to allow constant time access both by key (agent) and by value (position). There are two kind of Grid based on density, SparseGrid2D and DenseGrid2D.

  • NumberGrid2D, a simpler version of the Grid2D to use with simpler values. This is useful to represent simulation spaces covered by a simple entity that can be represented with a non-agent structure. This data structure can be used with any structure that can be cloned, most notably simple primitive values such as f32s. As the previous grid, there are two implementations: SparseNumberGrid2D and DenseNumberGrid2D.

  • Network and HNetwork to connect any kind of nodes using Edge/HEdge. With Network you can define both directed and undirected graphs and connect a couple of nodes with an edge with label and/or weight. HNetwork is a generalization of a Network to represent hypergraph. In this case, HEdge is an HashSet of nodes. With this fields you can reproduce any kind of graph or network, such as for our example Virus on a Network.


Support conference paper§

If you find this code useful in your research, please consider citing:

@ARTICLE{AntelmiASIASIM2019,
  author={Antelmi, A. and Cordasco, G. and D’Auria, M. and De Vinco, D. and Negro, A. and Spagnuolo, C.},
  title={On Evaluating Rust as a Programming Language for the Future of Massive Agent-Based Simulations},
  journal={Communications in Computer and Information Science},
  note={Conference of 19th Asia Simulation Conference, AsiaSim 2019 ; Conference Date: 30 October 2019 Through 1 November 2019;  Conference Code:233729},
  year={2019},
  volume={1094},
  pages={15-28},
  doi={10.1007/978-981-15-1078-6_2},
  issn={18650929},
  isbn={9789811510779},
}