Source code for evolib.operators.evonet_structural_mutation

# SPDX-License-Identifier: MIT

import numpy as np
from evonet.core import Nnet
from evonet.mutation import (
    add_random_connection,
    add_random_neuron,
    remove_random_connection,
    remove_random_neuron,
)

from evolib.config.base_component_config import StructuralMutationConfig


[docs] def mutate_structure(net: Nnet, cfg: StructuralMutationConfig) -> bool: """ Applies structural mutation operators to the EvoNet. Returns ------- bool True if a significant topological mutation occurred. """ structure_mutated = False # Collect all eligible mutation types (based on probability) ops = [] if ( cfg.add_connection is not None and cfg.add_connection.probability is not None and np.random.rand() < cfg.add_connection.probability ): ops.append("add_connection") if ( cfg.remove_connection is not None and cfg.remove_connection.probability is not None and np.random.rand() < cfg.remove_connection.probability ): ops.append("remove_connection") if ( cfg.add_neuron is not None and cfg.add_neuron.probability is not None and np.random.rand() < cfg.add_neuron.probability ): ops.append("add_neuron") if ( cfg.remove_neuron is not None and cfg.remove_neuron.probability is not None and np.random.rand() < cfg.remove_neuron.probability ): ops.append("remove_neuron") # Nothing triggered if not ops: return False # Choose one mutation type to apply op = np.random.choice(ops) # Add Connection if op == "add_connection": add_cfg = cfg.add_connection if add_cfg is not None: if ( cfg.topology.max_connections is None or len(net.get_all_connections()) < cfg.topology.max_connections ): allowed_kinds = set(cfg.topology.recurrent) for _ in range(np.random.randint(1, add_cfg.max + 1)): if add_random_connection( net, allowed_recurrent=allowed_kinds, connection_init=add_cfg.init, ): structure_mutated = True # Remove Connection elif op == "remove_connection": rem_cfg = cfg.remove_connection if rem_cfg is not None: for _ in range(np.random.randint(1, rem_cfg.max + 1)): if remove_random_connection(net): structure_mutated = True # Add Neuron elif op == "add_neuron": addn_cfg = cfg.add_neuron if cfg.topology.max_connections is not None: max_connections = max(0, cfg.topology.max_connections - net.num_weights) else: max_connections = 2**63 - 1 if addn_cfg is not None: if ( cfg.topology.max_neurons is None or net.num_hidden < cfg.topology.max_neurons ): if add_random_neuron( net=net, activations=addn_cfg.activations_allowed, connection_init=addn_cfg.init, connection_scope=cfg.topology.connection_scope, connection_density=addn_cfg.init_connection_ratio, max_connections=max_connections, ): structure_mutated = True # Remove Neuron elif op == "remove_neuron": remn_cfg = cfg.remove_neuron if remn_cfg is not None: if remove_random_neuron(net): structure_mutated = True return structure_mutated