88 lines
3.6 KiB
Python
88 lines
3.6 KiB
Python
import random
|
||
from typing import Sequence
|
||
|
||
from .node import Node
|
||
from .primitive import Primitive
|
||
|
||
|
||
class Chromosome:
|
||
def __init__(
|
||
self,
|
||
terminals: Sequence[Primitive],
|
||
operations: Sequence[Primitive],
|
||
root: Node,
|
||
):
|
||
self.terminals = terminals
|
||
self.operations = operations
|
||
self.root = root
|
||
|
||
def copy(self) -> Chromosome:
|
||
return Chromosome(self.terminals, self.operations, self.root.copy_subtree())
|
||
|
||
def prune(self, max_depth: int) -> None:
|
||
self.root.prune(self.terminals, max_depth)
|
||
|
||
def __str__(self) -> str:
|
||
"""Строковое представление хромосомы в виде формулы в инфиксной форме."""
|
||
return str(self.root)
|
||
|
||
@classmethod
|
||
def full_init(
|
||
cls,
|
||
terminals: Sequence[Primitive],
|
||
operations: Sequence[Primitive],
|
||
max_depth: int,
|
||
) -> Chromosome:
|
||
"""Полная инициализация.
|
||
|
||
В полном методе при генерации дерева, пока не достигнута максимальная глубина,
|
||
допускается выбор только функциональных символов, а на последнем уровне
|
||
(максимальной глубины) выбираются только терминальные символы.
|
||
"""
|
||
|
||
def build(level: int) -> Node:
|
||
# Если достигнута максимальная глубина — выбираем терминал
|
||
if level == max_depth:
|
||
return Node(random.choice(terminals))
|
||
|
||
# Иначе выбираем операцию и создаём потомков
|
||
op = random.choice(operations)
|
||
node = Node(op)
|
||
for _ in range(op.arity):
|
||
node.add_child(build(level + 1))
|
||
return node
|
||
|
||
return cls(terminals, operations, build(1))
|
||
|
||
@classmethod
|
||
def grow_init(
|
||
cls,
|
||
terminals: Sequence[Primitive],
|
||
operations: Sequence[Primitive],
|
||
max_depth: int,
|
||
# min_depth: int, # ???
|
||
terminal_probability: float = 0.5,
|
||
) -> Chromosome:
|
||
"""Растущая инициализация.
|
||
|
||
В растущей инициализации генерируются нерегулярные деревья с различной глубиной
|
||
листьев вследствие случайного на каждом шаге выбора функционального
|
||
или терминального символа. Здесь при выборе терминального символа рост дерева
|
||
прекращается по текущей ветви и поэтому дерево имеет нерегулярную структуру.
|
||
"""
|
||
|
||
def build(level: int) -> Node:
|
||
# Если достигнута максимальная глубина, либо сыграла заданная вероятность
|
||
# — выбираем терминал
|
||
if level == max_depth or random.random() < terminal_probability:
|
||
return Node(random.choice(terminals))
|
||
|
||
# Иначе выбираем случайную операцию и создаём потомков
|
||
op = random.choice(operations)
|
||
node = Node(op)
|
||
for _ in range(op.arity):
|
||
node.add_child(build(level + 1))
|
||
return node
|
||
|
||
return cls(terminals, operations, build(1))
|