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
68
figures/plot_E14.py
Normal file
68
figures/plot_E14.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
"""E14 figure — mating systems: monogamy vs promiscuity (mate-pool breadth) across ruggedness.
|
||||
|
||||
Three panels, each vs mate-pool breadth (log x: 0.03 = monogamous/structured -> 1.0 = promiscuous/
|
||||
panmictic), one line per landscape ruggedness K:
|
||||
|
||||
(A) best fitness / global optimum — the *champion*. On smooth landscapes (low K) it is maximised by
|
||||
wide breadth; as ruggedness rises the peak shifts to an INTERMEDIATE breadth (full promiscuity
|
||||
prematurely converges below it) — the mating-system image of E9's "optimal recombination rate
|
||||
shrinks with ruggedness".
|
||||
(B) mean fitness / global optimum — the *typical* individual. Monotonically favoured by breadth at
|
||||
every K: panmixia lifts the whole population toward a good consensus.
|
||||
(C) diversity (mean normalised pairwise Hamming) — monotonically DESTROYED by breadth at every K
|
||||
(promiscuity homogenises), the reservoir largest under monogamy and on rugged landscapes.
|
||||
|
||||
The tension between (A)/(C) is the result: promiscuity maximises the typical model and kills diversity;
|
||||
on rugged landscapes the best model needs preserved diversity, so an intermediate breadth wins.
|
||||
|
||||
Usage: python figures/plot_E14.py
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
from _figlib import load_bundle, savefig # noqa: E402
|
||||
|
||||
|
||||
def main() -> None:
|
||||
df, _ = load_bundle("results/E14")
|
||||
last = df[df["generation"] == df["generation"].max()].copy()
|
||||
last["best_n"] = last["best_fitness"] / last["global_opt"]
|
||||
last["mean_n"] = last["mean_fitness"] / last["global_opt"]
|
||||
Ks = sorted(last["K"].unique())
|
||||
cmap = plt.get_cmap("viridis")
|
||||
colors = {K: cmap(i / max(1, len(Ks) - 1)) for i, K in enumerate(Ks)}
|
||||
|
||||
fig, axes = plt.subplots(1, 3, figsize=(16, 5))
|
||||
panels = [
|
||||
("best_n", "best fitness / global optimum", "(A) the champion: best model in the population",
|
||||
"best fitness peaks at INTERMEDIATE breadth\non rugged landscapes (the peak shifts left as K rises)"),
|
||||
("mean_n", "mean fitness / global optimum", "(B) the typical model: population mean",
|
||||
"monotonically favoured by wide breadth\n(panmixia lifts the whole population)"),
|
||||
("diversity", "diversity (mean pairwise Hamming)", "(C) standing diversity",
|
||||
"monotonically destroyed by breadth\n(promiscuity homogenises; monogamy preserves)"),
|
||||
]
|
||||
for ax, (col, ylab, title, subtitle) in zip(axes, panels):
|
||||
for K in Ks:
|
||||
g = (last[last["K"] == K].groupby("breadth")[col]
|
||||
.agg(["mean", "sem"]).reset_index())
|
||||
ax.errorbar(g["breadth"], g["mean"], yerr=1.96 * g["sem"].fillna(0.0),
|
||||
marker="o", lw=1.8, capsize=2, color=colors[K], label=f"K={K}")
|
||||
ax.set_xscale("log")
|
||||
ax.set(xlabel="mate-pool breadth (monogamous ← → promiscuous)", ylabel=ylab)
|
||||
ax.set_title(f"{title}\n{subtitle}", fontsize=9)
|
||||
ax.legend(title="ruggedness", frameon=False, fontsize=8)
|
||||
|
||||
fig.suptitle("E14 — monogamy vs promiscuity: the best mate-pool breadth shrinks as skills get more entangled",
|
||||
y=1.02, fontsize=13)
|
||||
fig.tight_layout()
|
||||
savefig(fig, "results/E14", "E14")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue