The Theory Benchmark

Task: controlling number of flips in RLS on LeadingOnes
Cost: number of iterations until solution
Number of hyperparameters to control: one float
State Information: user specified, highly flexible
Noise Level: fairly large
Instance space: different instantiations of LeadingOnes

This benchmark is considered one of our highly controllable ones as there is ground truth available. It is also, however, built on top of the RLS algorithm, so not an artificial benchmark. At each step, the DAC controller chooses how many solution bits to flip. We want to optimize how many algorithm steps are taken, so the number of iterations is the reward.

While this is not an easy to solve benchmark, it is cheap to run and interfaces a real EA. Thus it may be a good entry point for harder EA-based benchmarks and also a good benchmark for analyzing controller behavior.

The Theory benchmark was constructed by Biedenkapp et al. for the paper `”Theory-Inspired Parameter Control Benchmarks for Dynamic Algorithm Configuration” <https://arxiv.org/pdf/2202.03259.pdf>`_ at GECCO 2022

class dacbench.benchmarks.theory_benchmark.TheoryBenchmark(config=None)

Bases: AbstractBenchmark

Benchmark with various settings for (1+(lbd, lbd))-GA and RLS

create_observation_space_from_description(obs_description, env_class=<class 'dacbench.envs.theory.RLSEnvDiscrete'>)

Create a gym observation space (Box only) based on a string containing observation variable names, e.g. “n, f(x), k, k_{t-1}” Return:

A gym.spaces.Box observation space

get_environment(test_env=False)

Return an environment with current configuration

Parameters:
test_env: whether the enviroment is used for train an agent or for testing.
if test_env=False:

cutoff time for an episode is set to 0.8*n^2 (n: problem size) if an action is out of range, stop the episode immediately and return a large negative reward (see envs/theory.py for more details)

otherwise: benchmark’s original cutoff time is used, and out-of-range action will be clipped to nearest valid value and the episode will continue.

read_instance_set()
Read instance set from file

we look at the current directory first, if the file doesn’t exist, we look in <DACBench>/dacbench/instance_sets/theory/

class dacbench.envs.theory.BinaryProblem(n, rng=Generator(PCG64) at 0x7F03439146D8)

Bases: object

An abstract class for an individual in binary representation

combine(xprime, locs_xprime)

combine (crossover) self and xprime by taking xprime’s bits at locs_xprime and self’s bits at other positions

Parameters
  • xprime (1d boolean array) – the individual to crossover with

  • locs_x (1d boolean/integer array) – positions where we keep current bits of self

  • locs_xprime (: 1d boolean/integer array) – positions where we change to xprime’s bits

Returns: the new individual after the crossover

crossover(xprime, p, n_childs, include_xprime=True, count_different_inds_only=True, rng=Generator(PCG64) at 0x7F02E3D2E6D8)
Crossover operator:

for each bit, taking value from x with probability p and from self with probability 1-p

Arguments:

x: the individual to crossover with p (float): in [0,1]

flip(locs)

flip the bits at position indicated by locs

Parameters

locs (1d-array) – positions where bits are flipped

Returns: the new individual after the flip

get_fitness_after_crossover(xprime, locs_x, locs_xprime)

Calculate fitness of the child aftering being crossovered with xprime

Parameters
  • xprime (1d boolean array) – the individual to crossover with

  • locs_x (1d boolean/integer array) – positions where we keep current bits of self

  • locs_xprime (: 1d boolean/integer array) – positions where we change to xprime’s bits

get_fitness_after_flipping(locs)

Calculate the change in fitness after flipping the bits at positions locs

Parameters

locs (1d-array) – positions where bits are flipped

objective after flipping

mutate(p, n_childs, rng=Generator(PCG64) at 0x7F02E3D2E4F8)

Draw l ~ binomial(n, p), l>0 Generate n_childs children by flipping exactly l bits Return: the best child (maximum fitness), its fitness and number of evaluations used

mutate_rls(l, rng=Generator(PCG64) at 0x7F02E3D2E5E8)

generate a child by flipping exactly l bits Return: child, its fitness

class dacbench.envs.theory.LeadingOne(n, rng=Generator(PCG64) at 0x7F02E3D2E7C8, initObj=None)

Bases: BinaryProblem

An individual for LeadingOne problem The aim is to maximise the number of leading (and consecutive) 1 bits in the string

get_fitness_after_crossover(xprime, locs_x, locs_xprime)

Calculate fitness of the child aftering being crossovered with xprime

Parameters
  • xprime (1d boolean array) – the individual to crossover with

  • locs_x (1d boolean/integer array) – positions where we keep current bits of self

  • locs_xprime (: 1d boolean/integer array) – positions where we change to xprime’s bits

get_fitness_after_flipping(locs)

Calculate the change in fitness after flipping the bits at positions locs

Parameters

locs (1d-array) – positions where bits are flipped

objective after flipping

class dacbench.envs.theory.RLSEnv(config, test_env=False)

Bases: AbstractEnv

Environment for RLS with step size Current assumption: we only consider (1+1)-RLS, so there’s only one parameter to tune (r)

close() bool

Close Env

No additional cleanup necessary

Returns

Closing confirmation

Return type

bool

get_obs_domain_from_name()

Get default lower and upperbound of a observation variable based on its name. The observation space will then be created Return:

Two int values, e.g., 1, np.inf

reset()

Resets env

Returns

Environment state

Return type

numpy.array

step(action)

Execute environment step

Parameters

action (Box) – action to execute

Returns

  • state, reward, done, info

  • np.array, float, bool, dict

class dacbench.envs.theory.RLSEnvDiscrete(config, test_env=False)

Bases: RLSEnv

RLS environment where the choices of r is discretised

step(action)

Execute environment step

Parameters

action (Box) – action to execute

Returns

  • state, reward, done, info

  • np.array, float, bool, dict