Source code for gefest.core.opt.operators.crossovers

import copy
import random
from enum import Enum
from functools import partial
from itertools import product
from typing import Callable

import numpy as np
from loguru import logger

from gefest.core.geometry import Point, Polygon, Structure
from gefest.core.geometry.domain import Domain
from gefest.core.utils import where


[docs] def crossover_structures( structure1: Structure, structure2: Structure, domain: Domain, operations: list[Callable], operation_chance: float, operations_probs: list[int], **kwargs, ) -> tuple[Structure]: """Applys random crossover from given list for pair of structures. Args: structure1 (Structure): First parent. structure1 (Structure): Second parent. domain (Domain): Task domain. operations (list[Callable]): List of crossovers operations to choose. operation_chance (float): Chance of crossover. operations_probs (list[int]): Probablilites of each crossover operation. Returns: tuple[Structure]: Сhildren. """ s1, s2 = copy.deepcopy(structure1), copy.deepcopy(structure2) chosen_crossover = np.random.choice( a=operations, size=1, p=operations_probs, ) new_structure = chosen_crossover[0](s1, s2, domain) if not new_structure: logger.warning(f'None out: {chosen_crossover[0].__name__}') return new_structure
# pairs for crossover selection
[docs] def panmixis(pop: list[Structure]) -> list[tuple[Structure, Structure]]: """Default pair selection strategy.""" np.random.shuffle(list(pop)) return [(pop[idx], pop[idx + 1]) for idx in range(len(pop) - 1)]
# best indivisual selection
[docs] def structure_level_crossover( s1: Structure, s2: Structure, domain: Domain, **kwargs, ): """Exchanges points of two polygons.""" s1, s2 = copy.deepcopy(s1), copy.deepcopy(s2) polygons1 = s1.polygons polygons2 = s2.polygons crossover_point = np.random.randint( 0, len(polygons1) + 1, ) # Crossover conversion part_1 = polygons1[0:crossover_point] if not isinstance(part_1, tuple): part_1 = part_1 part_2 = polygons2[crossover_point : len(s1.polygons)] if not isinstance(part_2, tuple): part_2 = part_2 result = list(copy.deepcopy(part_1)) result.extend(copy.deepcopy(part_2)) new_structure = Structure(polygons=result) return (new_structure,)
[docs] def polygon_level_crossover( s1: Structure, s2: Structure, domain: Domain, **kwargs, ): """Exchanges points of two nearest polygons in structure.""" geom = domain.geometry s1, s2 = copy.deepcopy(s1), copy.deepcopy(s2) intersected = False split_angle = 0 pairs_dists = [ (p, geom.min_distance(p[0], p[1])) for p in list(product(s1, s2)) if (p[0] is not p[1] and len(p[0]) != 0 and len(p[1]) != 0) ] intersects_ids = where(pairs_dists, lambda p_d: p_d[1] == 0) if len(intersects_ids) > 0: intersected = True pairs_dists = [p_d for idx_, p_d in enumerate(pairs_dists) if idx_ in intersects_ids] pairs_dists = [ (p[0], geom.min_distance(geom.get_centroid(p[0][0]), geom.get_centroid(p[0][1]))) for p in pairs_dists ] pairs_dists = sorted(pairs_dists, key=lambda p_d: p_d[1]) if len(pairs_dists) == 0: return (s1,) poly_1 = pairs_dists[0][0][0] poly_2 = pairs_dists[0][0][1] if intersected: # now not adaptive angle # split_angle = (np.random.rand() * 2 - 1) * (70) elif pairs_dists[0][1] > domain.dist_between_polygons: return (s1,) c1, c2 = geom.get_centroid(poly_1), geom.get_centroid(poly_2) vector1 = geom.rotate_point(point=c2, origin=c1, angle=split_angle) vector2 = geom.rotate_point(point=c1, origin=c2, angle=split_angle) scale_factor = max(domain.max_x, domain.max_y) parts_1 = geom.split_polygon(poly_1, [c1, vector1], scale_factor) parts_2 = geom.split_polygon(poly_2, [c2, vector2], scale_factor) if len(parts_1) < 2 or len(parts_2) < 2: return (s1,) new_parts = (*parts_1[0], *parts_2[1]) if c1.y > c2.y else (*parts_1[1], *parts_2[0]) new_poly = geom.get_convex(Polygon(points=[Point(*p) for p in list(set(new_parts))])) if len(new_poly) > domain.max_points_num: random_elements = random.choice(new_poly.points) new_poly.points = list(filter(lambda x: x != random_elements, new_poly.points)) idx_ = where(s1.polygons, lambda p: p == poly_1)[0] s1[idx_] = new_poly return (s1,)
[docs] class CrossoverTypes(Enum): """Enumerates all crossover functions.""" structure_level = partial(structure_level_crossover) polygon_level = partial(polygon_level_crossover)