society: multi-locus recombination frame — the vertical claim (E7/E8)
Enter the Lamarckian society with a robust theoretical frame. The single-
locus, fixed-p* model can only express recovery toward a ceiling; the
society's load-bearing claim is vertical -- capability that EXCEEDS any
component. Generalize knowledge to a distribution over genotypes (L
biallelic loci, K=2^L, additive fitness = # correct loci), reusing all the
K-mode machinery. The one new operator is recombination: free recombination
sends p -> product of per-locus marginals (linkage equilibrium).
E8 (star, kind: society) -- the vertical claim / Fisher-Muller: decorrelated
PARENTS (specialists, expert on their loci, agnostic elsewhere) are
recombined; sexual merge assembles a genotype fitter than any parent,
climbing to the optimum (12/12, a genotype no parent had) as parent count
grows and rho->0, while the best single parent (~8.7) and the mean-mixture
"model soup" (~11.6) plateau below. Reuses make_retention_matrix (locus
mastery replaces tail-item retention).
E7 (kind: genotype_lineage) -- the advantage of sex: a single population
adapts toward the optimum; the sexual lineage adapts faster than asexual
(clonal interference) by keeping loci in linkage equilibrium (LD->0 vs LD
spike). Honest scope: a speed advantage, not a permanent Muller's-ratchet
gap (subtle to force); E8 carries the headline.
Metaphor shift (per GG): the society is sexual reproduction with UNBOUNDED
parents, not teacher->pupil. Teacher->pupil caps at the ceiling; n-parent
recombination is combinatorial and generative, and unlike biology there is
no two-parent limit. Collapse = asexual degradation; the cure = sex. This
unifies E4 (merge != average) + E6 (irreversibility) under evolution-of-sex
theory and reaches ground Riis's single-locus n-grams cannot.
New: knowledge/{genotype,genotype_lineage,society}.py, configs/layer1/{E7,
E8}.yaml, figures/plot_{E7,E8}.py, READMEs, tests/test_genotype.py (+7).
experiment.py dispatch (kind in {genotype_lineage, society}); make layer1
wired. 112 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
871bc39ec6
commit
62c68d6c8c
22 changed files with 879 additions and 3 deletions
|
|
@ -127,6 +127,46 @@ def run_experiment(cfg: dict) -> pd.DataFrame:
|
|||
return out
|
||||
|
||||
|
||||
_GENOTYPE_KEYS = ("genotype", "generations")
|
||||
|
||||
|
||||
def run_genotype_experiment(cfg: dict) -> pd.DataFrame:
|
||||
"""Run a genotype lineage across a sweep x replicates (E7, advantage of sex).
|
||||
|
||||
Mirrors ``run_experiment`` (paired replicate seeds) but assembles the base from the
|
||||
``genotype``/``generations`` blocks and calls ``run_genotype_lineage``. Sweeps use the same
|
||||
dotted-path ``_apply_param`` (e.g. ``genotype.recomb_rate`` for asexual vs sexual).
|
||||
"""
|
||||
from .genotype_lineage import run_genotype_lineage
|
||||
|
||||
base = {k: copy.deepcopy(cfg[k]) for k in _GENOTYPE_KEYS if k in cfg}
|
||||
sweeps = cfg.get("sweep", [])
|
||||
if isinstance(sweeps, dict):
|
||||
sweeps = [sweeps]
|
||||
params = [s["param"] for s in sweeps]
|
||||
value_lists = [list(s["values"]) for s in sweeps]
|
||||
combos = [({}, base)] if not sweeps else []
|
||||
for values in itertools.product(*value_lists):
|
||||
lin = copy.deepcopy(base)
|
||||
label: dict = {}
|
||||
for param, val in zip(params, values):
|
||||
label.update(_apply_param(lin, param, val))
|
||||
combos.append((label, lin))
|
||||
|
||||
seeds = spawn_seeds(int(cfg["seed"]), int(cfg["n_replicates"]))
|
||||
frames: list[pd.DataFrame] = []
|
||||
for label, lin in combos:
|
||||
for rep, ss in enumerate(seeds):
|
||||
df = run_genotype_lineage(lin, int(ss.generate_state(1)[0]))
|
||||
for col, val in label.items():
|
||||
df[col] = val
|
||||
df["replicate"] = rep
|
||||
frames.append(df)
|
||||
out = pd.concat(frames, ignore_index=True)
|
||||
out.insert(0, "experiment", cfg["experiment"])
|
||||
return out
|
||||
|
||||
|
||||
def run_coverage(cfg: dict) -> pd.DataFrame:
|
||||
"""E4 runner: multi-teacher recombination coverage (blueprint 2.5-E4 / 2.7.1).
|
||||
|
||||
|
|
@ -278,7 +318,16 @@ def run_and_save(config_path: str | Path) -> Path:
|
|||
config_path = Path(config_path)
|
||||
cfg = yaml.safe_load(config_path.read_text())
|
||||
out_dir = Path(cfg.get("output", {}).get("dir", f"results/{cfg['experiment']}"))
|
||||
df = run_coverage(cfg) if cfg.get("kind") == "coverage" else run_experiment(cfg)
|
||||
kind = cfg.get("kind", "lineage")
|
||||
if kind == "coverage":
|
||||
df = run_coverage(cfg)
|
||||
elif kind == "genotype_lineage":
|
||||
df = run_genotype_experiment(cfg) # E7: advantage of sex
|
||||
elif kind == "society":
|
||||
from .society import run_society # E8: multi-parent recombination
|
||||
df = run_society(cfg)
|
||||
else:
|
||||
df = run_experiment(cfg)
|
||||
save_artifacts(cfg, df, out_dir)
|
||||
return out_dir
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue