OverviewFilesCommitsIssuesDocsReleasesPeers

basal-agent / selfbuild.c

89 lines · c ref:
/* selfbuild.c — does rung 3 BUILD rung 4? (self-building test, not hand-coding)
 *
 * Claim: from rung 3 (replicators + open-ended evolution) the ladder builds its own
 * next rung. The honest test is not to hand-code compartments but to show they are the
 * SELECTED solution to a problem rung 3 alone cannot solve — the PARASITE (Eigen's
 * paradox): fast free-riders take over a shared pool and cooperation collapses.
 *
 * Cooperators (C) sustain the system but replicate slower; parasites (P) replicate
 * faster and contribute nothing. Compartment viability depends on its cooperators
 * (an all-parasite compartment is dead). Two levels of selection:
 *   within compartment  -> favors P (parasite replicates faster)   [rung 3 alone]
 *   between compartment -> favors C (only C-bearing compartments persist) [rung 4]
 * Szathmary-Demeter stochastic corrector: with SMALL compartments the between-level
 * variance lets selection maintain C; with LARGE compartments (-> well-mixed) the
 * within-level (parasite) wins and C is lost. So compartmentalization is not given —
 * it is SELECTED because non-compartmentalized lineages collapse.
 *
 * Sweep compartment size D. Small D (strong compartmentalization) rescues cooperation;
 * large D (~well-mixed, rung 3 alone) loses it. The transition IS rung 3 selecting rung 4.
 * Caveat: we supply the OPTION to compartmentalize (the law-space); evolution supplies
 * the selection. Self-building = selection over inherited possibilities.
 *
 * build:  cc -O2 -Wall -std=c11 -o selfbuild selfbuild.c -lm
 */
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#define NCOMP 400        /* number of compartments (fixed population) */
#define GEN   300        /* generations */
#define SADV  0.5        /* parasite replication advantage (P is 1+SADV x faster) */

static unsigned long long RNG;
static unsigned long long xs(void){ RNG^=RNG<<13; RNG^=RNG>>7; RNG^=RNG<<17; return RNG; }
static double urand(void){ return (double)(xs()>>11)*(1.0/9007199254740992.0); }

static int nC[NCOMP];            /* cooperators in each compartment (rest are parasites) */
static int dC[2*NCOMP];          /* daughter compartments' cooperator counts */
static double cum[2*NCOMP];

/* returns steady-state global cooperator fraction for compartment size D */
static double run(int D){
	for(int c=0;c<NCOMP;c++) nC[c]=D/2;      /* start half cooperators */
	double acc=0; int accn=0;
	for(int g=0;g<GEN;g++){
		for(int c=0;c<NCOMP;c++){
			int nc=nC[c], np=D-nc;
			double wc=nc, wp=np*(1.0+SADV);
			double pC=(wc+wp>0)? wc/(wc+wp) : 0;
			/* grow D -> 2D by replication (parasite advantage) */
			int g2=0; for(int k=0;k<2*D;k++) if(urand()<pC) g2++;
			/* divide: hypergeometric split of 2D (g2 cooperators) into two D daughters */
			int rc=g2, rt=2*D, d1=0;
			for(int k=0;k<D;k++){ if(rt>0 && urand() < (double)rc/rt){ d1++; rc--; } rt--; }
			dC[2*c]=d1; dC[2*c+1]=g2-d1;
		}
		/* between-compartment selection: keep NCOMP daughters weighted by cooperator count
		 * (all-parasite daughters have weight 0 -> die). This is the rung-4 level. */
		double tot=0; for(int i=0;i<2*NCOMP;i++){ tot+=dC[i]; cum[i]=tot; }
		if(tot<=0){ for(int c=0;c<NCOMP;c++) nC[c]=0; }   /* cooperators extinct */
		else for(int c=0;c<NCOMP;c++){
			double r=urand()*tot; int lo=0,hi=2*NCOMP-1;
			while(lo<hi){ int mid=(lo+hi)/2; if(cum[mid]<r) lo=mid+1; else hi=mid; }
			nC[c]=dC[lo];
		}
		if(g>=GEN-60){ long s=0; for(int c=0;c<NCOMP;c++) s+=nC[c]; acc+=(double)s/((double)NCOMP*D); accn++; }
	}
	return acc/accn;
}

int main(void){
	printf("=== SELF-BUILDING TEST: does rung 3 evolve rung 4 (compartments)? ===\n");
	printf("  parasite advantage %.0f%%; cooperation survives only if compartmentalization is selected\n\n",SADV*100);
	printf("  %-14s %8s %16s\n","compartment D","coop frac","regime");
	int Ds[]={2,3,4,6,10,16,30,60,120};
	for(int i=0;i<9;i++){
		RNG=0x9e3779b97f4a7c15ULL + (unsigned long long)Ds[i]*0x2545F4914F6CDD1DULL;
		double cf=run(Ds[i]);
		const char *reg = cf>0.3? "cooperation RESCUED" : cf>0.1? "marginal" : "COLLAPSED (parasites win)";
		int bar=(int)(cf*40+0.5); char b[64]; for(int k=0;k<bar&&k<50;k++)b[k]='#'; b[bar<50?bar:50]=0;
		printf("  D=%-12d %8.3f  %-22s %s\n",Ds[i],cf,reg,b);
	}
	printf("\n  Small D (strong compartmentalization): cooperation is maintained.\n");
	printf("  Large D (-> well-mixed = rung 3 alone): parasites win, cooperation COLLAPSES.\n");
	printf("  => compartmentalization is the SELECTED solution — rung 3 builds rung 4.\n");
	printf("     (We supplied the option to compartmentalize; evolution supplied the selection.)\n");
	return 0;
}