AHD-CMA / src /ahdcma /controller /entropy.py
Akiyue's picture
Add files using upload-large-folder tool
f325414 verified
Raw
History Blame Contribute Delete
1.92 kB
"""Population diversity entropy.
Per Research Proposal §4 controller, the population's diversity is
summarised as the **sum of per-dimension Shannon entropies** of the
histograms over each search-space coordinate. Higher entropy means the
swarm is spread out (favouring exploitation if the landscape allows);
lower entropy signals collapse onto a single basin (a cue to switch into
exploration).
"""
from __future__ import annotations
import numpy as np
from numpy.typing import NDArray
_LOG2 = np.log(2.0)
def population_entropy(
population: NDArray[np.float64],
*,
bins: int = 10,
bounds: tuple[float, float] = (0.0, 1.0),
) -> float:
"""Sum of per-dimension Shannon entropies (in bits).
Parameters
----------
population : NDArray[np.float64]
Array of shape ``(n, d)`` with rows being individuals.
bins : int
Histogram bin count per dimension. Default ``10`` matches the
controller config.
bounds : tuple[float, float]
Histogram range applied to every dimension. Default ``(0, 1)``
matches the unit-cube convention used throughout the project.
Returns
-------
float
Non-negative scalar in ``[0, d * log2(bins)]``. Returns ``0.0``
for a degenerate population (all individuals at the same point).
"""
p = np.asarray(population, dtype=np.float64)
if p.ndim != 2:
raise ValueError(f"population must be 2D (n, d); got shape {p.shape}")
n, d = p.shape
if n == 0 or d == 0:
return 0.0
if bins < 2:
raise ValueError(f"bins must be >= 2, got {bins}")
total = 0.0
for j in range(d):
counts, _ = np.histogram(p[:, j], bins=bins, range=bounds)
probs = counts.astype(np.float64) / float(n)
nonzero = probs[probs > 0]
if nonzero.size > 0:
total += float(-np.sum(nonzero * np.log(nonzero)) / _LOG2)
return total