Source code for metaheuristic_designer.initializer

"""
Base class for the Initializer module.

This module implements functions to generate the initial population of the algorithm.
"""

from __future__ import annotations
import inspect
from typing import Any, Optional, Callable
from abc import ABC, abstractmethod
import numpy as np
from .population import Population
from .encoding import Encoding, DefaultEncoding
from .objective_function import ObjectiveFunc
from .utils import check_rng, RNGLike, VectorLike


[docs] class Initializer(ABC): """Abstract base for all population initializers. An initializer creates the first generation of individuals. It must provide a way to generate a single random genotype vector (a 1-D NumPy array) via :meth:`generate_random` and can optionally wrap it with a different definition of an individual via :meth:`generate_individual`. Parameters ---------- dimension : int Length of the genotype vector. population_size : int, optional Number of individuals to generate (default 1). encoding : Encoding, optional Encoding that will be attached to every individual. Defaults to :class:`DefaultEncoding`. rng : RNGLike, optional Random number generator. """ def __init__(self, dimension: int, population_size: int = 1, encoding: Optional[Encoding] = None, rng: Optional[RNGLike] = None): self.dimension = dimension self.population_size = population_size if encoding is None: encoding = DefaultEncoding() self.encoding = encoding self.rng = check_rng(rng)
[docs] @abstractmethod def generate_random(self) -> VectorLike: """Generate a single random genotype vector (1-D array). Returns ------- VectorLike A newly generated genotype vector (1-D array). """
[docs] def generate_individual(self) -> VectorLike: """Generate a single individual. By default simply delegates to :meth:`generate_random`. Returns a newly generated individual (a 1-D array). Override this method if your initializer needs to distinguish between a randomly initialize individual and a solution generated with another strategy (See `SeedProbInitializer`). Returns ------- Any A newly generated individual. """ return self.generate_random()
[docs] def generate_population(self, n_individuals: Optional[int] = None) -> Population: """ Create a fully formed population of *n_individuals* individuals. Parameters ---------- n_individual: int, optional Number of individuals to generate Returns ------- generated_population: Population Newly generated population. """ if n_individuals is None: n_individuals = self.population_size population_matrix = np.atleast_2d([self.generate_individual() for _ in range(n_individuals)]) return Population(genotype_matrix=population_matrix, encoding=self.encoding)
[docs] def get_state(self) -> dict: """Return a minimal dictionary identifying this initializer. Returns ------- dict Dictionary with key ``"class_name"``. """ data = {"class_name": self.__class__.__name__} return data
[docs] class InitializerFromLambda(Initializer): """Initializer that uses a user-provided function to generate individuals. Parameters ---------- generator : callable A function ``(rng) -> genotype`` that returns a single genotype vector. dimension : int Length of the genotype vector. pop_size : int, optional Number of individuals to generate (default 1). encoding : Encoding, optional Encoding attached to every individual. rng : RNGLike, optional Random number generator. """ def __init__(self, generator: Callable, dimension: int, pop_size: int = 1, encoding: Optional[Encoding] = None, rng: Optional[RNGLike] = None): self._validate_function(generator) self.generator = generator super().__init__(dimension=dimension, population_size=pop_size, encoding=encoding, rng=rng) @staticmethod def _validate_function(fn: Callable): operator_sig = inspect.signature(fn) count = 0 for p in operator_sig.parameters.values(): if p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD: count += 1 elif p.kind == inspect.Parameter.VAR_POSITIONAL: return required_min_count = 1 if count < required_min_count: raise TypeError(f"The function should have at least a positional argument (`rng`).")
[docs] def generate_random(self) -> VectorLike: return self.generator(rng=self.rng)