File size: 1,926 Bytes
590a501
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
"""Genetic operators: selection, crossover, mutation."""

from __future__ import annotations

import random

from factor_engine.gp.operators import (
    AbsOp,
    Neg,
    RankCS,
    TsDecayLinear,
    TsEMA,
    TsMean,
    TsRank,
    TsSlope,
    TsStd,
    TsZScore,
    ZScoreCS,
    choose_binary_operator,
    generate_random_tree,
    make_binary,
    make_unary,
)


def tree_too_large(tree, max_depth, max_nodes):
    return tree.get_depth() > max_depth or tree.get_size() > max_nodes


def tournament_selection(population, fitnesses, k=5):
    idxs = random.sample(range(len(population)), k)
    best_idx = max(idxs, key=lambda i: fitnesses[i])
    return population[best_idx].clone()


def replace_random_subtree(tree, new_subtree):
    tree = tree.clone()
    nodes = tree.get_nodes()
    if len(nodes) <= 1:
        return new_subtree.clone()
    target = random.choice(nodes)
    if target is tree:
        return new_subtree.clone()
    for node in nodes:
        if hasattr(node, "children"):
            for i, child in enumerate(node.children):
                if child is target:
                    node.children[i] = new_subtree.clone()
                    return tree
    return tree


def mutate(tree, max_init_depth):
    if random.random() < 0.55:
        new_subtree = generate_random_tree(1, max_depth=random.randint(2, max(2, max_init_depth - 1)))
        return replace_random_subtree(tree, new_subtree)
    if random.random() < 0.85:
        child = tree.clone()
        op = random.choice([AbsOp, Neg, RankCS, ZScoreCS, TsMean, TsStd, TsZScore, TsRank, TsDecayLinear, TsEMA, TsSlope])
        return make_unary(op, child)
    return make_binary(choose_binary_operator(), tree.clone(), generate_random_tree(1, max_depth=3))


def crossover(parent1, parent2):
    p1 = parent1.clone()
    donor = random.choice(parent2.clone().get_nodes()).clone()
    return replace_random_subtree(p1, donor)