Genetic algorithm optimization

In this tutorial, we will demonstrate how to use the genetic algorithm (GA) optimization functionality in larvaworld, plus we will have a look on how it operates behind the scenes.

Here is a definition : The GA will optimize an existing larva model by adjusting some set of its parameters within a defined space in order to generate behavior resembling as close as possible some reference experimental dataset according to some defined evaluation metrics.

This sounds rather complex, so we will break it down!

Let’s import the relevant classes :

%load_ext param.ipython
import panel as pn

pn.extension()

from larvaworld.lib import reg
from larvaworld.lib.sim.genetic_algorithm import (
    GAevaluation,
    GAselector,
    GAlauncher,
    GAconf,
)
from larvaworld.lib.model.modules.module_modes import SpaceDict

# Tutorial safety switches (avoid heavy compute / GUI by default)
RUN_GA_DEMO = False
RUN_GA_VIDEO_DEMO = False

DEMO_EXP = "realism"
DEMO_DURATION_MIN = 0.1
DEMO_NGENERATIONS = 1
DEMO_NAGENTS = 10  # keep >= 7 when selection_ratio=0.3
SAVE_MEDIA = False
MEDIA_DIR = "./media"

A look at the GA configuration class makes it easy to get an idea of the involved arguments.

Leaving aside the general simulation arguments and the environment configuration we will focus on the GA evaluation and selection configurations.

BTW one of the preconfigured GA experiments can be called via the experiment argument.

# Show the attributes of the GAlauncher class
%params GAconf

# Show the attributes of the GAlauncher class as a nested dictionary
GAconf.param

The optimization target of the GA is an already existing larva model, stored in the Model configuration database under a unique ID. It is provided as an argument to the GA during initialization.

The fields of this model that should be used to create the parameter optimization space are also provided by the respective module names.

The creation and management of the parameter space is done via a dedicated class

# Show the attributes of the SpaceDict class
%params SpaceDict

# Show the attributes of the SpaceDict class as a nested dictionary
SpaceDict.param
# Create a SpaceDict object
space_dict = SpaceDict(base_model="explorer", space_mkeys=["interference", "crawler"])

Alongside the above parameters that define the optimization space, the GAselector class manages the number and size of the GA’s generations and the selection algorithm that governs the creation of each subsuquent generation from the previous.

# Show the attributes of the GAselector class
%params GAselector

# Show the attributes of the GAselector class as a nested dictionary
GAselector.param

We are now turning to the other crucial set of arguments for the GA, namely the reference dataset and evaluation process.

The reference dataset should be selected either via a reference ID or via the directory where it is located.

The evaluation process is specified via a number of evaluation metrics, meaning kinematic angular, translational or temporal parameters that will be used to evaluate the behavior of each virtual larva against the reference dataset. Additionally there is the option of trying to fit some parameters of the stride-cycle curve.

All these are collectively referred to as GA evaluation arguments :

# Show the attributes of the GAevaluation class
%params GAevaluation

# Show the attributes of the GAevaluation class as a nested dictionary
GAevaluation.param

Finally, we can create a GAlauncher object and run the genetic algorithm.

Here we will make use of a stored GA configuration and just adjust some of its parameters.

The simulation returns a dictionary containing the optimization space, fitness achieved and best genome

# A pre-defined GA experiment
exp = DEMO_EXP

# Create a GAlauncher object with short demo settings
ga1 = GAlauncher(
    experiment=exp, duration=DEMO_DURATION_MIN, screen_kws={"show_display": False}
)

# Modify some GA selection parameters
ga1.selector.Ngenerations = DEMO_NGENERATIONS
ga1.selector.Nagents = DEMO_NAGENTS

# Launch the GA simulation (may still take time depending on the experiment)
if RUN_GA_DEMO:
    best1 = ga1.simulate()

    # Inspect the optimization results
    best1.keylist

    # The optimized larva-model
    best1.mConf.print()

Alternatively the stored GA configuration can be retrieved and modified before providing it to the launcher.

In this case the parameters argument is used.

Additionally here the simulation is visualized.

# A pre-defined GA experiment
exp = DEMO_EXP

# Retrieve the stored configuration
p = reg.conf.Ga.expand(exp)

# Modify some GA selection parameters
p.ga_select_kws.Ngenerations = DEMO_NGENERATIONS
p.ga_select_kws.Nagents = DEMO_NAGENTS

# Optional: export a video (headless)
if RUN_GA_VIDEO_DEMO:
    screen_kws = {
        "vis_mode": "video",
        "show_display": False,
        "save_video": SAVE_MEDIA,
        "media_dir": MEDIA_DIR,
        "video_file": f"ga_{exp}",
    }
    ga2 = GAlauncher(parameters=p, duration=DEMO_DURATION_MIN, screen_kws=screen_kws)
    best2 = ga2.simulate()

    # Inspect the optimization results
    best2.keylist

    # The optimized larva-model
    best2.mConf.print()