E14: mating systems — monogamy vs promiscuity (mate-pool breadth)
A new analytic experiment on an orthogonal evolution-of-sex axis: not the recombination RATE (E9) but the population's mating STRUCTURE. Agents on a ring recombine with a second parent drawn from a window of breadth b (b->0 monogamous/isolation-by-distance, b=1 promiscuous/panmictic), under local selection, swept against NK ruggedness K. Finding: the optimal mate-pool breadth SHRINKS as skills get more entangled. Wide/promiscuous merging wins the champion on additive landscapes (K<=3, b=0.6), but on rugged ones (K>=6) it prematurely converges to a worse champion and an intermediate breadth (b~0.35) wins; pure monogamy over-fragments. Throughout, promiscuity monotonically lifts the population MEAN but destroys diversity and parallel exploration. The design rule extends E9: merge widely for additive skills, keep island-structured sub-populations for entangled ones — a merging-native axis the panmixia-assuming literature lacks. - src/knowledge/mating_system.py + experiment.py dispatch (kind: mating_system) - configs/layer1/E14.yaml (breadth x K sweep, 20 reps, bitwise-reproducible) - figures/plot_E14.py; results/E14/ (figure, README, manifest, resolved config) - tests/test_mating_system.py (+5, 147 green); make layer1 wired - folded into both papers (full + accessible) as the third §5 result Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
9fea375ff8
commit
f5f68f5249
13 changed files with 472 additions and 2 deletions
57
tests/test_mating_system.py
Normal file
57
tests/test_mating_system.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
"""Mating-system tests (pure NumPy) — monogamy vs promiscuity as mate-pool breadth (E14).
|
||||
|
||||
Cover the diversity helpers and the two load-bearing behaviours: promiscuity (wide mate-pool breadth)
|
||||
monotonically destroys standing diversity, and the run is deterministic and well-formed. The full
|
||||
ruggedness crossover (intermediate breadth wins the champion on rugged landscapes) is a swept,
|
||||
multi-replicate result asserted only in aggregate here to keep the test fast.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
from knowledge.mating_system import _distinct_peaks, _diversity, run_mating_system
|
||||
from knowledge.genotype import nk_fitness
|
||||
|
||||
|
||||
def _run(breadth: float, K: int = 6, seed: int = 0, gens: int = 40, N: int = 32, L: int = 10):
|
||||
cfg = {"mating": {"L": L, "N": N, "breadth": breadth, "K": K, "recomb_rate": 0.5, "mu": 0.005},
|
||||
"generations": gens}
|
||||
return run_mating_system(cfg, seed=seed)
|
||||
|
||||
|
||||
def test_diversity_zero_for_clones_and_positive_for_spread():
|
||||
clones = np.ones((5, 8), dtype=np.int8)
|
||||
assert _diversity(clones) == 0.0 # identical -> no diversity
|
||||
spread = np.array([[0] * 8, [1] * 8], dtype=np.int8)
|
||||
assert np.isclose(_diversity(spread), 1.0) # opposite -> maximal diversity
|
||||
|
||||
|
||||
def test_distinct_peaks_counts_basins():
|
||||
fitness = nk_fitness(6, 2, seed=0)
|
||||
pop = np.zeros((4, 6), dtype=np.int8) # all identical -> one basin
|
||||
assert _distinct_peaks(pop, fitness, 6) == 1
|
||||
|
||||
|
||||
def test_schema_and_bounds():
|
||||
df = _run(0.5)
|
||||
for col in ["generation", "best_fitness", "mean_fitness", "diversity", "distinct_peaks", "global_opt"]:
|
||||
assert col in df.columns
|
||||
assert (df["best_fitness"] <= df["global_opt"] + 1e-9).all() # nothing beats reality's optimum
|
||||
assert (df["diversity"] >= 0).all() and (df["diversity"] <= 1).all()
|
||||
assert df["distinct_peaks"].iloc[-1] >= 1
|
||||
|
||||
|
||||
def test_deterministic_given_seed():
|
||||
a = _run(0.3, seed=7)
|
||||
b = _run(0.3, seed=7)
|
||||
assert np.allclose(a["best_fitness"], b["best_fitness"]) # pure function of the seed
|
||||
|
||||
|
||||
def test_promiscuity_destroys_diversity():
|
||||
# Averaged over replicates, wide mate-pool breadth (promiscuity) leaves LESS standing diversity than
|
||||
# narrow breadth (monogamy) — the homogenisation effect, robust on a rugged landscape.
|
||||
def final_div(b):
|
||||
return np.mean([_run(b, K=8, seed=s, gens=40, N=32, L=10)["diversity"].iloc[-1]
|
||||
for s in range(6)])
|
||||
assert final_div(1.0) < final_div(0.05) # panmixia < isolation-by-distance
|
||||
Loading…
Add table
Add a link
Reference in a new issue