Source code for saealib.operators.dedup
"""Duplicate elimination operator for offspring filtering."""
from __future__ import annotations
from dataclasses import dataclass
import numpy as np
from saealib.core.contracts import (
MANY,
ComponentContract,
DataSpec,
PortContract,
PortDirection,
PortSpec,
Var,
)
[docs]
@dataclass
class DuplicateElimination:
"""Filter offspring that duplicate any member of the current population.
Parameters
----------
atol : float
Absolute tolerance passed to ``numpy.isclose``.
Matches the ``atol`` semantics of
:class:`~saealib.population.archive.ArchiveMixin`.
rtol : float
Relative tolerance passed to ``numpy.isclose``.
Matches the ``rtol`` semantics of
:class:`~saealib.population.archive.ArchiveMixin`.
max_retries : int
Maximum number of regeneration attempts per
:meth:`~saealib.algorithms.ga.GA.ask` call.
"""
atol: float = 1e-16
rtol: float = 0.0
max_retries: int = 100
[docs]
def contract(self) -> ComponentContract:
"""Return the duplicate-filter contract."""
representation = Var(name="R")
return ComponentContract(
ports={
"duplicate_filter": PortContract(
inputs=(
PortSpec(
name="offspring",
direction=PortDirection.INPUT,
data=DataSpec(
kind="GenomeBatch",
bindings={"representation": representation},
),
cardinality=MANY,
),
PortSpec(
name="population",
direction=PortDirection.INPUT,
data=DataSpec(
kind="GenomeBatch",
bindings={"representation": representation},
),
cardinality=MANY,
),
),
outputs=(
PortSpec(
name="duplicates",
direction=PortDirection.OUTPUT,
data=DataSpec(kind="RowPredicate"),
cardinality=MANY,
),
),
)
}
)
[docs]
def find_duplicates(self, off_x: np.ndarray, pop_x: np.ndarray) -> np.ndarray:
"""Return a boolean mask of offspring that duplicate the population.
Parameters
----------
off_x : numpy.ndarray of shape (N, dim)
Offspring decision vectors.
pop_x : numpy.ndarray of shape (M, dim)
Current population decision vectors.
Returns
-------
numpy.ndarray of shape (N,), dtype bool
``True`` where ``off_x[i]`` is element-wise close to any row of
``pop_x``, using the same ``atol`` / ``rtol`` as
:meth:`~saealib.population.archive.ArchiveMixin._find_idx`.
"""
close = np.all(
np.isclose(pop_x[None], off_x[:, None], atol=self.atol, rtol=self.rtol),
axis=2,
)
return np.any(close, axis=1)