Linear Genetic Programming

Linear genetic programming (LGP) is an evolutionary programming paradigm. It represents the program as a sequence of instructions, where each sequence is either an operation

r_0 \leftarrow \textrm{add}\, (r_1,c_1)

or a control structure

\textbf{if}\; \textit{condition}\; \textbf{then}\; \{ \cdots\}

Exploring the Backstage

[1]:
import evokit.evolvables as evolvables
[2]:
import evokit.evolvables.lgp as lgp
from evokit.evolvables.lgp import cells
[3]:
program = lgp.RegisterStates(registers = (3, 4, 5, 6),
                            constants = (7, 8, 9, 10, 11))
print(program)
LGP execution context here. Register states:
> r(3, 4, 5, 6), c(7, 8, 9, 10, 11)
[4]:
from evokit.evolvables.primitives import add, sub, mul, div, sin, cos

import random
ASSURANCE_CASE_COUNT = 20
REGISTER_COUNT = 2
CONSTANT_COUNT = 4
registerses = [[random.random() for _ in range(REGISTER_COUNT)] for _ in range(ASSURANCE_CASE_COUNT)]
constantses = [(0., 1., 2., -1.) for _ in range(ASSURANCE_CASE_COUNT)]

def demo_function(registers: list[float], constants: tuple[float,
                                                           float,
                                                           float,
                                                           float]) -> list[float]:
    r = registers.copy()
    c = constants

    r[0] = r[0] * c[1]
    r[0] = r[0] + c[1]
    if r[1] < c[0]:
        r[1] = sin(r[0])
        r[1] = r[1] * 5
    return r

inputs = list(zip(registerses, constantses))

outputs = [[demo_function(registers, constants)[0]]
           for registers, constants in inputs]
[5]:
fitness_cases = list(zip(inputs,
                         outputs))

Benchmark (just one, need to add more)

[6]:
from evokit.evolvables.lgp import LGPEvaluator, LinearGeneticProgram, Crossover, LGPFactory

from evokit.core import Population
from evokit.evolvables.algorithms import SimpleLinearAlgorithm

from evokit.evolvables.lgp import LGPEvaluator, Crossover

from evokit.evolvables.selectors import TruncationSelector
from typing import Sequence
def maybe_mse(xs: Sequence[float],
              ys: Sequence[float]):
    return -sum((abs(x - y)**2
                for x, y in zip(xs, ys)))







IND_SIZE = 2

POP_SIZE = 40

factora = LGPFactory(
    primitives=[add, sub, mul, div, sin, cos],
    register_count=REGISTER_COUNT,
    constant_count=CONSTANT_COUNT,
)

OUTPUT_INDEX = 1

pop: Population[LinearGeneticProgram] = Population(
        [factora.build_fully_effective(segment_length=IND_SIZE,
                                       output_indices={0})
         for _ in range(POP_SIZE)]
)

evaluator = LGPEvaluator(fitness_cases=list(fitness_cases),
             fitness_function=maybe_mse,
             optimise_mode="mask",
             output_indices={1},)

variator = Crossover(k=4,
                     allow_repeat=False,
                     even=False)

selector = TruncationSelector(budget=POP_SIZE)


algo = SimpleLinearAlgorithm(population=pop,
                             variator=variator,
                             evaluator=evaluator,
                             selector=selector)
[7]:
from typing import Callable
import time
from evokit.watch.watchers import create_fitness_watcher
from evokit.watch.visual import plot


WATCHER_TIMER: Callable[[], float] = time.perf_counter

default_watcher_settings = {
    "events": "STEP_END",
    "watch_post_step":True,
    "timer": WATCHER_TIMER
}

fitness_watcher = create_fitness_watcher(**default_watcher_settings)
algo.register(fitness_watcher)
[8]:
for _ in range(90):
    algo.step()
[9]:
algo.population[9].genome
[9]:
[]
[10]:
fitness_watcher[0]
[10]:
WatcherRecord(event='POST_STEP', generation=0, value=(-19.3960483362172,), time=794071.5762742)
[11]:
plot(fitness_watcher, show_generation=True, use_line=True)
../../_images/guides_examples_lgp_15_0.png