Files
genetic-algorithms/lab4/gp/chromosome.py
2025-11-05 20:07:35 +03:00

114 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 shrink_mutation(self) -> None:
"""Усекающая мутация. Заменяет случайно выбранную операцию на случайный терминал."""
operation_nodes = [n for n in self.root.list_nodes() if n.value.arity > 0]
if not operation_nodes:
return
target_node = random.choice(operation_nodes)
target_node.prune(self.terminals, max_depth=1)
def grow_mutation(self, max_depth: int) -> None:
"""Растущая мутация. Заменяет случайно выбранный узел на случайное поддерево."""
target_node = random.choice(self.root.list_nodes())
max_subtree_depth = max_depth - target_node.get_level() + 1
subtree = Chromosome.grow_init(
self.terminals, self.operations, max_subtree_depth
).root
if target_node.parent:
target_node.parent.replace_child(target_node, subtree)
else:
self.root = subtree
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))