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)