society: the dynamic Lamarckian society — the vertical claim (E11 / C3)
The culmination. A finite population of agents (genotypes, L loci) evolves
on a rugged NK landscape that IS reality (knowledge/dynamic_society.py),
composing the four operators the whole study built toward: grounding,
directed recombination (sex), quality-diversity selection, and mutation.
Grounding is made load-bearing via the consensus-conformity (self-
consumption) mechanism (GG decision): selection acts on
g*true_fitness + (1-g)*conformity, where conformity = agreement with the
population's own consensus, so at g=0 the society optimises fitting-the-
crowd rather than reality.
4-arm ablation (12 reps), each breaking distinctly, only the full society
climbing (global_opt ~ 0.79):
- full 0.78 climbs to the optimum, diversity maintained longest
- no_sex 0.77 can't recombine to escape local optima
- no_diversity 0.74 greedy: collapses diversity fastest, worse local optimum
- no_grounding 0.48 self-consumption collapse to an unfit consensus
(trains on the crowd -> confident-but-wrong mean;
conformity-true gap ~ 0.5)
This integrates E1-E6 + the learning kernel + E7-E10 into one system and
shows the Lamarckian society needs ALL of grounding + directed sex +
diversity: on a rugged landscape you need diversity to explore basins, sex
to recombine them, and grounding to select on reality -- remove any one and
you fail differently. Closes the C3 vertical claim analytically; the LLM
rung remains the eventual empirical instantiation.
New: knowledge/dynamic_society.py, configs/layer1/E11.yaml, figures/
plot_E11.py, README, tests/test_dynamic_society.py (+5). kind:
dynamic_society dispatch; make layer1 wired. 122 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
48181a1c84
commit
0f7b775ae5
13 changed files with 460 additions and 5 deletions
58
tests/test_dynamic_society.py
Normal file
58
tests/test_dynamic_society.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
"""Dynamic-society tests (pure NumPy) — the culminating vertical claim (E11 / C3).
|
||||
|
||||
Cover the finite-population operators (consensus, conformity, novelty) and the four ablation
|
||||
behaviours: the full society climbs to near the optimum; removing grounding collapses it to an unfit
|
||||
consensus (self-consumption); removing sex or diversity leaves it stuck below the full society.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
from knowledge.dynamic_society import _conformity, _consensus, _novelty, run_dynamic_society
|
||||
|
||||
|
||||
def _run(arm_overrides: dict, seed: int = 0, gens: int = 50):
|
||||
base = {"L": 10, "K": 6, "N": 50, "g": 0.85, "mu": 0.03, "novelty": 0.5,
|
||||
"n_off": 100, "recomb_rate": 0.2, "sex": True, "select": "qd"}
|
||||
base.update(arm_overrides)
|
||||
return run_dynamic_society({"society": base, "generations": gens}, seed=seed)
|
||||
|
||||
|
||||
def test_consensus_and_conformity():
|
||||
pop = np.array([[1, 1, 0, 0], [1, 0, 0, 1], [1, 1, 1, 0]], dtype=np.int8)
|
||||
cons = _consensus(pop)
|
||||
assert np.array_equal(cons, [1, 1, 0, 0]) # majority vote per locus
|
||||
conf = _conformity(pop, cons)
|
||||
assert np.isclose(conf[0], 1.0) # agent 0 == consensus
|
||||
assert conf.min() >= 0.0 and conf.max() <= 1.0
|
||||
|
||||
|
||||
def test_novelty_is_zero_for_clones_and_high_for_spread():
|
||||
clones = np.ones((4, 8), dtype=np.int8)
|
||||
assert np.allclose(_novelty(clones), 0.0) # identical -> no diversity
|
||||
spread = np.array([[0] * 8, [1] * 8], dtype=np.int8)
|
||||
assert np.allclose(_novelty(spread), 1.0) # opposite -> maximal diversity
|
||||
|
||||
|
||||
def test_full_society_climbs_toward_optimum():
|
||||
df = _run({})
|
||||
go = df["global_opt"].iloc[0]
|
||||
assert df["best_fitness"].iloc[-1] > df["best_fitness"].iloc[0] + 0.05 # it climbs
|
||||
assert df["best_fitness"].iloc[-1] > 0.9 * go # ... to near the optimum
|
||||
|
||||
|
||||
def test_no_grounding_collapses_to_unfit_consensus():
|
||||
full = _run({})["best_fitness"].iloc[-1]
|
||||
dry = _run({"g": 0.0})
|
||||
assert dry["best_fitness"].iloc[-1] < full - 0.1 # far below the grounded society
|
||||
assert dry["diversity"].iloc[-1] < 0.05 # diversity collapsed
|
||||
assert dry["conformity_true_gap"].iloc[-1] > 0.3 # agreement >> real capability (delusion)
|
||||
|
||||
|
||||
def test_ablations_stay_below_the_full_society():
|
||||
full = _run({})["best_fitness"].iloc[-1]
|
||||
no_sex = _run({"sex": False})["best_fitness"].iloc[-1]
|
||||
no_div = _run({"select": "greedy", "novelty": 0.0})["best_fitness"].iloc[-1]
|
||||
assert no_sex <= full + 1e-6 and no_div <= full + 1e-6 # neither beats the full society
|
||||
assert min(no_sex, no_div) < full # ... and at least one is strictly worse
|
||||
Loading…
Add table
Add a link
Reference in a new issue