Usage Examples

Below we provide two example scripts that display a typical usage of freneticlib. The first one uses an executor featuring a bicycle model (BicycleExecutor), while the second one executes using the BeamNG.tech executor (BeamNGExecutor).

Bicycle Executor Example

import logging

from freneticlib.core.core import FreneticCore
from freneticlib.core.mutation import exploiters, crossovers
from freneticlib.core.mutation.mutators import FreneticMutator
from freneticlib.core.objective import MaxObjective
from freneticlib.executors.bicycle.bicycleexecutor import BicycleExecutor
from freneticlib.executors.road_validator import RoadValidator
from freneticlib.frenetic import Frenetic
from freneticlib.representations.kappa_representation import FixStepKappaRepresentation
from freneticlib.stopcriteria.counter import CountingStop

# specify a logging format
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s %(message)s", datefmt="%H:%M:%S")


def run_example():
    # We want a FixStep Kappa representation
    representation = FixStepKappaRepresentation(length=30, variation=5, step=10.0)
    # alternative:
    # representation = CatmullRomRepresentation(control_nodes=30, variation=5)

    # Setup an objective. Here: maximize the distance_from_center (i.e. push the vehicle off the road)
    objective = MaxObjective(
        feature="distance_from_center",
        # every simulation produces 10 records per second, we extract the maximum value of the selected feature
        per_simulation_aggregator="max",
    )

    # Define the Frenetic core using representation, objective and the mutation operators
    core = FreneticCore(
        representation=representation,
        objective=objective,
        mutator=FreneticMutator(),
        crossover=crossovers.ChooseRandomCrossoverOperator(size=20),
    )

    # Define the Frenetic executor and the stop-criterion.
    frenetic = Frenetic(
        core,
        executor=BicycleExecutor(
            representation=representation,
            objective=objective,
            # results_path="./data/detailed"
        ),
        stop_criterion=CountingStop(n_random=50, n_total=250),
    )

    # If we wanted to extend a previous run, we could load the history like so:
    # frenetic.load_history("./data/dev.csv")

    # run the search
    frenetic.start()

    # store the history for later use
    frenetic.store_results("./data/history.csv")

    # Display the progress
    frenetic.plot("./data/plot.png")


if __name__ == "__main__":
    run_example()

BeamNG.tech Executor Example

Our second example is rather similar to the first one, except that it uses a different simulator. However, to use the BeamNG.tech simulator, we first have to perform some extra steps.

  1. First, request a free researcher license from https://register.beamng.tech.

  2. Then, download and install the BeamNG.tech according to the instructions emailed to you.

  3. Next, download or clone the SBFT 2023 CPS tool competition pipeline, which we use as an interface to BeamNG.tech.

  4. Specify the required arguments cps_pipeline_path, beamng_home and beamng_user in the initialisation of the BeamNGExecutor, like so

BeamNGExecutor(
    representation=...,
    objective=...,
    cps_pipeline_path="~/cps-tool-competition",
    beamng_home="~/Downloads/BeamNG.tech.v0.26.2.0",
    beamng_user="~/BeamNG.tech",
)

The rest of the code is exactly the same as in the BicycleExecutor example.

Note

Currently, we tested frenetic-lib on Windows using BeamNG.tech v0.26.2.0 In case you use it on Linux and/or another version of BeamNG.tech, please share your experience with us.

import logging
from pathlib import Path

from freneticlib.core.core import FreneticCore
from freneticlib.core.mutation import exploiters, crossovers
from freneticlib.core.mutation.mutators import FreneticMutator
from freneticlib.core.objective import MaxObjective
from freneticlib.executors.beamng.beamng_executor import BeamNGExecutor
from freneticlib.executors.bicycle.bicycleexecutor import BicycleExecutor
from freneticlib.frenetic import Frenetic
from freneticlib.representations.kappa_representation import FixStepKappaRepresentation
from freneticlib.stopcriteria.counter import CountingStop

# specify a logging format
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s %(message)s", datefmt="%H:%M:%S")


def run_example():
    # We want a FixStep Kappa representation
    representation = FixStepKappaRepresentation(length=30, variation=5, step=10.0)

    # Setup an objective. Here: maximize the distance_from_center (i.e. push the vehicle off the road)
    objective = MaxObjective(
        feature="oob_percentage",  # BeamNG Executor
        # every simulation produces many records per second, we extract the maximum of this
        per_simulation_aggregator="max",
    )

    # Define the Frenetic core using representation, objective and the mutation operators
    core = FreneticCore(
        representation=representation,
        objective=objective,
        mutator=FreneticMutator(),
        crossover=crossovers.ChooseRandomCrossoverOperator(size=20),
    )

    # Define the Frenetic executor and the stop-criterion.
    frenetic = Frenetic(
        core,
        BeamNGExecutor(
            representation=representation,
            objective=objective,
            cps_pipeline_path="~/cps-tool-competition",
            beamng_home="~/Downloads/BeamNG.tech.v0.26.1.0",
            beamng_user="~/BeamNG.tech",
            results_path="./data/detailed",
        ),
        CountingStop(n_random=5, n_total=15),  # just a few, since simulation takes long
    )

    # run the search
    frenetic.start()

    # store the history for later use
    frenetic.store_results("./data/dev.csv")

    # Display the progress
    frenetic.plot()


if __name__ == "__main__":
    run_example()