Skip to content

Commit

Permalink
Merge pull request #31 from scioip34/feature/interactive-tool
Browse files Browse the repository at this point in the history
Feature/interactive tool
  • Loading branch information
mezdahun authored Feb 25, 2022
2 parents d81a8d3 + e424be1 commit 9d4f733
Show file tree
Hide file tree
Showing 11 changed files with 1,108 additions and 126 deletions.
46 changes: 42 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ This repository hold the code base for the agent based model framework implement
### Requirements
To run the simulations you will need python 3.8 or 3.9 and pip correspondingly. It is worth to set up a virtualenvironment using pipenv or venv for the project so that your global workspace is not polluted.

### Test
To test the code:
### Test Requirements
To test if all the requirements are ready to use:
1. Clone the repo
2. Activate your virtual environment (pipenv, venv) if you are using one
3. Move into the cloned repo where `setup.py` is located and run `pip install -e .` with that you installed the simulation package
4. run the start entrypoint of the simulation package by running `abm-start`
4. run the start entrypoint of the simulation package by running `playground-start` or `abm-start`
5. If you also would like to save data you will need an InfluxDB instance. To setup one, please follow the instructions below.
6. If you would like to run simulations in headless mode (without graphics) you will need to install xvfb first (only tested on Ubuntu) with `sudo apt-get install xvfb`. After this, you can start the simulation in headless mode by calling the `headless-abm-start` entrypoint instead of the normal `abm-start` entrypoint.

## Install Grafana and InfluxDB
To monitor individual agents real time and save simulation data (i.e. write simulation data real time and save upon request at the end) we use InfluxDB and a grafana server for visualization. For this purpose you will need to install influx and grafana. If you don't do these steps you are still going to be able to run simulations, but you won't be able to save the resulting data or visualize the agent's parameters. This installation guide is only tested on Ubuntu. If you decide to use another op.system or you don't want to monitor and save simulation data, set `USE_IFDB_LOGGING` and `SAVE_CSV_FILES` parameters in the `.env` file to `0`.
Expand Down Expand Up @@ -109,7 +111,8 @@ The package includes the following submodules:
* `loader`: including all classes and methods to dynamically load data that was generated with the package. These methods are for example cvs and json readers and initializers that initialize input classes for Replay and DataAnalysis tools.
* `metarunner`: including all classes and methods to run multiple simulations one after the other with programatically changed initialization parameters. The main classes are `Tunables` that define a criterion range with requested number of datapoints (e.g.: simulate with environment width from 300 to 600 via 4 datapoints). The `Constant` class that defines a fixed criterion throughout the simulations (e.g.: keep the simulation time `T` fixed at 1000). And `MetaProtocol` class that defines a batch of simulations with all combinations of the defined criteria according to the added `Tunable`s and `Constant`s. During running metaprotocols the corresponding initializations (as `.env` files) will be saved under the `data/metaprotocol/temp` folder. Only those `.env` files will be removed from here for which the simulations have been carried out, therefore the metaprotocol can be interrupted and finished later.
* `monitoring`: including all methods to interface with InfluxDB, Grafana and to save the stored data from the database at the end of the simulation. The data will be saved into the `data/simualtion_data/<timestamp_of_simulation>` of the root abm folder. The data will consist of 2 relevant csv files (agent_data, resource_data) containing time series of the agent and resource patch status and a json file containing all parameters of the simulation for reproducibility.
* `simulation`: including the main `Simulation` class that defines how the environment is visualized, what interactions the user can have with the pygame environment (e.g.: via cursor or buttons), and how the environment enforces some restrictions on agents, and how resources are regenerated.
* `simulation`: including the main `Simulation` class that defines how the environment is visualized, what interactions the user can have with the pygame environment (e.g.: via cursor or buttons), and how the environment enforces some restrictions on agents, and how resources are regenerated. Furthermore a `PlaygroundSimulation` class is dedicated to provide an interactive playground where the user can explore different parameter combinations with the help of sliders and buttons. This class inherits all of it's simulation functionality from the main `Simulation` class but might change the visualization and adds additional interactive optionalities. When the framework is started as a playground, the parameters in the `.env` file don't matter anymore, but a `.env` file is still needed in the main ABM folder so that the supercalss can be initiated.
* `replay`: to explore large batches of simulated experimental data, a replay class has been implemented. To initialize the class one needs to pass the absolute path of an experiment folder generated by the metaruneer tool. Upon initialization, in case the experiment is not yet summarized into numpy arrays this step is carried out. The arrays are then read back to the memory at once. The different batches and parameter combinations can be explored with interactive GUI elements. In case the amount of data is too large, one can use undersampling of data to only include every n-th timestep in the summary arrays.

### Functionality and Behavior
Here you can read about how the framework works in large scale behavior and what restrictions and assumptions we used throughout the simulation.
Expand Down Expand Up @@ -230,3 +233,38 @@ EXPERIMENT_NAME=exp4 python home/ABM/abm/data/metarunner/experiments/exp4.py
where we assume you have a `exp4.env` file in the root project folder (`home/ABM`) and you store an experiment file `exp4.py` under a dedicated path, in the example, this path is `home/ABM/abm/data/metarunner/experiments/`.

Note that the env variable `EXPERIMENT_NAME` is used to show the given `MetaProtocol` instance which `.env` file it needs to use (and replace during runs). Therefore it must have a scope ONLY for the given command. If you set this varaible globally on your OS then all `MetaProtocol` instances will try to use and replace the same `.env` file and therefore during parallel runs unwanted behavior and corrupted data states can occur. The given commands are only to be used on Linux.

### Interactive Exploration Tool
To allow users quick-and-dirty experimentation with the model framework, an interactive playground tool has been implemented. This can be started with `playground-start` after preparing the environment as described above.

Once the playground tool has been started a window will pop up with a simulation arena on the upper right part with a given number of agnets and resources. Parameters are initialized according to the `contrib` package. These parameters can be tuned with interactive sliders on the right side of the window. To get some insights of these parameters see the env variable descriptions above or click and hold the `?` buttons next to the sliders.

#### Resource number and radius
When changing the number of resource patches and their radii, the tool automatically adjusts these to each other so that the total covered area in the arena will not exceed 30% of the arena surface. This is necessary as resources are initialized in a way that no overlap is present.

#### Fixed Overall Resource Units
When starting the tool the overall amount of resource units (summed over all patches of the arena) is fixed and can be controlled with the `SUM_R` slider. Changing this value will redistribute the amount of units between the patches in a way that the ratio of units in between tha patches will not change, and the depletion level of the patches also stays the same. In case this feature is turned off with the corresponding action button below the simulation arena, increasing the number of resource patches will increase the overall number of resources in the environment.

#### Detailed Information
To get more detailed information about resource patches and agents, click and hold them with the left mouse button. Note that this alos moves the agents. Other interactions such as rotating agents, pausing the simulation, etc. are the same as in the original simulation class. In case you would like to get an insight about all agents and resources use the corresponding action button under the simulation area. Note that this can slow down the simulation significantly due to the amount of text to be rendered on the screen.

#### Video Recording
To show the effect of parameter combinations and make experiments reproducable, you can also record a short video of particularly interesting phenomena. To do so, use the `Record Video` action button under the simulation arena. When the recording is started, the button turns red as well as a red "Rec" dot will pop up in the upper left corner. When you stop the recording with the same action button, the tool will save and compress the resulting video and save in the data folder of the package. Please note that this might take a few minutes for longer videos.

#### Other Function Buttons
Some boolean parameters can be turned on and off with the help of additional function buttons (below the visualization area). These are
* Turn on Ghost Mode: overalpping on the patches are allowed
* Turn on IFDB logging: in case a visualization through the grafana interface is required one can start IFDB logging with this button. By default it is turned off so that we can avoid a database writing overhead and the tool can be aslo started without IFDB installed on the system.
* Turn on Visual Occlusion: in case it is turned on, agents can occlude visual cues from farther away agants.

### Replay Tool
To visualize large batches of data generated as experiment folders with the metarunner tool, one can use the replay tool. A demonstrative script has been provided in the repo to show how one can start such a replay of experiment.

#### Behavior
Upon start if the experiment was not summarized before into numpy arrays this will be done. Then these arrays are read back to the memory to initialize the GUI of the tool. On the left side an arena shows the agnets and resources. Below, global statistics are shown in case it is requested with the `Show Stats` action button. The path of the agents as well as their visual field can be visualized with the corresponding buttons. Note that interactions from the simulation or the playground tool won't work here as this visualization will be a pure replay (as in a replayed video) of the recorded simulation itself. One can replay the recorded data in time with the `Start/Stop` button or by moving the time slider.

#### Parameter Combinations
Possible parameter combinations are read automatically from the data and the corresponding sliders will be initialized in the action area accordingly. By that, one can go through the simulated parameter combinations and different batches by moving the sliders on the right.

#### Plotting
To plot some global statistics of the data corresponding action buttons have been implemented on the right. Note that it only works with 1, 2 or 3 changed parameters. In case 3 parameters were tuned throughout the experiment one can either plot multiple 2 dimensional figures or "collapse" the plot along an axis using some method, such as minimum or maximum collision. This means that along that axis instead of taking all values into consideration one will onmly take the max or min of the values. This is especially useful when 2 parameters were tuned together in a way that their product should remain the same (That can be done adding so called Tuned Pairs to the criterion of the metarunner tool). In these cases only specified parameter combinations have informative values and not the whole parameter space provided with the parameter ranges.
18 changes: 14 additions & 4 deletions abm/agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,15 @@ def __init__(self, id, radius, position, orientation, env_size, color, v_field_r
self.position = np.array(position, dtype=np.float64) # saved
self.orientation = orientation # saved
self.color = color
self.selected_color = colors.LIGHT_BLUE
self.v_field_res = v_field_res
self.pooling_time = pooling_time
self.pooling_prob = pooling_prob
self.consumption = consumption
self.vision_range = vision_range
self.visual_exclusion = visual_exclusion
self.FOV = FOV
self.show_stats = False

# Non-initialisable private attributes
self.velocity = 0 # agent absolute velocity # saved
Expand Down Expand Up @@ -95,6 +97,8 @@ def __init__(self, id, radius, position, orientation, env_size, color, v_field_r
self.g_u = decision_params.g_u
self.B_u = decision_params.B_u
self.u_max = decision_params.u_max
self.F_N = decision_params.F_N
self.F_R = decision_params.F_R

# Pooling attributes
self.time_spent_pooling = 0 # time units currently spent with pooling the status of given position (changes
Expand Down Expand Up @@ -133,7 +137,7 @@ def calc_I_priv(self):
collected_unit = self.collected_r - self.collected_r_before

# calculating private info by weighting these
self.I_priv = decision_params.F_N * np.max(self.novelty) + decision_params.F_R * collected_unit
self.I_priv = self.F_N * np.max(self.novelty) + self.F_R * collected_unit

def move_with_mouse(self, mouse, left_state, right_state):
"""Moving the agent with the mouse cursor, and rotating"""
Expand Down Expand Up @@ -264,9 +268,15 @@ def draw_update(self):
self.image = pygame.Surface([self.radius * 2, self.radius * 2])
self.image.fill(colors.BACKGROUND)
self.image.set_colorkey(colors.BACKGROUND)
pygame.draw.circle(
self.image, self.color, (self.radius, self.radius), self.radius
)
if self.is_moved_with_cursor:
pygame.draw.circle(
self.image, self.selected_color, (self.radius, self.radius), self.radius
)
else:
pygame.draw.circle(
self.image, self.color, (self.radius, self.radius), self.radius
)

# showing agent orientation with a line towards agent orientation
pygame.draw.line(self.image, colors.BACKGROUND, (self.radius, self.radius),
((1 + np.cos(self.orientation)) * self.radius, (1 - np.sin(self.orientation)) * self.radius),
Expand Down
83 changes: 51 additions & 32 deletions abm/app.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,62 @@
from contextlib import ExitStack

from abm.simulation.sims import Simulation
from abm.simulation.isims import PlaygroundSimulation

from xvfbwrapper import Xvfb

import os
# loading env variables from dotenv file
from dotenv import dotenv_values

EXP_NAME = os.getenv("EXPERIMENT_NAME", "")


def start(parallel=False):
def start(parallel=False, headless=False):
root_abm_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
envconf = dotenv_values(os.path.join(root_abm_dir, f"{EXP_NAME}.env"))
sim = Simulation(N=int(float(envconf["N"])),
T=int(float(envconf["T"])),
v_field_res=int(envconf["VISUAL_FIELD_RESOLUTION"]),
agent_fov=float(envconf['AGENT_FOV']),
framerate=int(float(envconf["INIT_FRAMERATE"])),
with_visualization=bool(int(float(envconf["WITH_VISUALIZATION"]))),
width=int(float(envconf["ENV_WIDTH"])),
height=int(float(envconf["ENV_HEIGHT"])),
show_vis_field=bool(int(float(envconf["SHOW_VISUAL_FIELDS"]))),
show_vis_field_return=bool(int(envconf['SHOW_VISUAL_FIELDS_RETURN'])),
pooling_time=int(float(envconf["POOLING_TIME"])),
pooling_prob=float(envconf["POOLING_PROBABILITY"]),
agent_radius=int(float(envconf["RADIUS_AGENT"])),
N_resc=int(float(envconf["N_RESOURCES"])),
min_resc_perpatch=int(float(envconf["MIN_RESOURCE_PER_PATCH"])),
max_resc_perpatch=int(float(envconf["MAX_RESOURCE_PER_PATCH"])),
min_resc_quality=float(envconf["MIN_RESOURCE_QUALITY"]),
max_resc_quality=float(envconf["MAX_RESOURCE_QUALITY"]),
patch_radius=int(float(envconf["RADIUS_RESOURCE"])),
regenerate_patches=bool(int(float(envconf["REGENERATE_PATCHES"]))),
agent_consumption=int(float(envconf["AGENT_CONSUMPTION"])),
ghost_mode=bool(int(float(envconf["GHOST_WHILE_EXPLOIT"]))),
patchwise_exclusion=bool(int(float(envconf["PATCHWISE_SOCIAL_EXCLUSION"]))),
teleport_exploit=bool(int(float(envconf["TELEPORT_TO_MIDDLE"]))),
vision_range=int(float(envconf["VISION_RANGE"])),
visual_exclusion=bool(int(float(envconf["VISUAL_EXCLUSION"]))),
show_vision_range=bool(int(float(envconf["SHOW_VISION_RANGE"]))),
use_ifdb_logging=bool(int(float(envconf["USE_IFDB_LOGGING"]))),
save_csv_files=bool(int(float(envconf["SAVE_CSV_FILES"]))),
parallel=parallel
)
vscreen_width = int(float(envconf["ENV_WIDTH"])) + 100
vscreen_height = int(float(envconf["ENV_HEIGHT"])) + 100
with ExitStack() if not headless else Xvfb(width=vscreen_width, height=vscreen_height) as xvfb:
sim = Simulation(N=int(float(envconf["N"])),
T=int(float(envconf["T"])),
v_field_res=int(envconf["VISUAL_FIELD_RESOLUTION"]),
agent_fov=float(envconf['AGENT_FOV']),
framerate=int(float(envconf["INIT_FRAMERATE"])),
with_visualization=bool(int(float(envconf["WITH_VISUALIZATION"]))),
width=int(float(envconf["ENV_WIDTH"])),
height=int(float(envconf["ENV_HEIGHT"])),
show_vis_field=bool(int(float(envconf["SHOW_VISUAL_FIELDS"]))),
show_vis_field_return=bool(int(envconf['SHOW_VISUAL_FIELDS_RETURN'])),
pooling_time=int(float(envconf["POOLING_TIME"])),
pooling_prob=float(envconf["POOLING_PROBABILITY"]),
agent_radius=int(float(envconf["RADIUS_AGENT"])),
N_resc=int(float(envconf["N_RESOURCES"])),
min_resc_perpatch=int(float(envconf["MIN_RESOURCE_PER_PATCH"])),
max_resc_perpatch=int(float(envconf["MAX_RESOURCE_PER_PATCH"])),
min_resc_quality=float(envconf["MIN_RESOURCE_QUALITY"]),
max_resc_quality=float(envconf["MAX_RESOURCE_QUALITY"]),
patch_radius=int(float(envconf["RADIUS_RESOURCE"])),
regenerate_patches=bool(int(float(envconf["REGENERATE_PATCHES"]))),
agent_consumption=int(float(envconf["AGENT_CONSUMPTION"])),
ghost_mode=bool(int(float(envconf["GHOST_WHILE_EXPLOIT"]))),
patchwise_exclusion=bool(int(float(envconf["PATCHWISE_SOCIAL_EXCLUSION"]))),
teleport_exploit=bool(int(float(envconf["TELEPORT_TO_MIDDLE"]))),
vision_range=int(float(envconf["VISION_RANGE"])),
visual_exclusion=bool(int(float(envconf["VISUAL_EXCLUSION"]))),
show_vision_range=bool(int(float(envconf["SHOW_VISION_RANGE"]))),
use_ifdb_logging=bool(int(float(envconf["USE_IFDB_LOGGING"]))),
save_csv_files=bool(int(float(envconf["SAVE_CSV_FILES"]))),
parallel=parallel
)
sim.write_batch_size = 100
sim.start()


def start_headless():
start(headless=True)


def start_playground():
sim = PlaygroundSimulation()
sim.start()
Loading

0 comments on commit 9d4f733

Please sign in to comment.