OverviewFilesCommitsIssuesDocsReleasesPeers

hypercore-kb / basal-agent / rung45dup.c

81 lines · c ref:
/* rung45dup.c — 4->5 in a real population, WITH duplication (the rung-2 copying capacity).
 *
 * rung45 stalled: specialists arose only by CONVERTING generalists (displacement) at fixed
 * compartment size -> viability drops -> purged. Here we give the population DUPLICATION — the
 * copying capacity that already emerged at rung 2 — so a generalist can spawn a SPECIALIST
 * offspring without losing itself (the compartment GROWS). Specialization becomes an ADDITION
 * (neutral), not a displacement. A size cost in selection then pressures shedding of redundant
 * generalists once specialists cover the functions. Does division of labor now EMERGE in a real
 * evolving population (not the idealized Metropolis search of bridge.c)?
 *
 * compartment = role counts n[0..F] (0 generalist, 1..F specialists), variable size.
 * viability = min_k(n[k] + EGEN*n[0])  (need ALL functions). selection ~ viability/(1+cost*size).
 * grow (specialists replicate faster) -> duplication mutation adds specialists -> divide -> select.
 *
 * build:  cc -O2 -Wall -std=c11 -o rung45dup rung45dup.c -lm
 */
#include <math.h>
#include <stdio.h>

#define NCOMP 400
#define GEN   500
#define F 3
#define EGEN 0.2
#define SSPEC 0.30       /* specialist replication advantage */
#define DMAX 24
#define MUDUP 0.06       /* generalist offspring specializes (duplication + divergence) */
#define MULOSS 0.02      /* specialist offspring reverts to generalist */
#define SIZECOST 0.06    /* per-member cost in selection (favors efficient, small compartments) */

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 n[NCOMP][F+1];
static int dau[2*NCOMP][F+1]; static double cum[2*NCOMP];

static int sizeof_(const int*a){ int s=0; for(int k=0;k<=F;k++) s+=a[k]; return s; }
static double viab(const int*a){ double m=1e9; for(int k=1;k<=F;k++){ double c=a[k]+EGEN*a[0]; if(c<m)m=c; } return m; }

int main(void){
	printf("=== RUNG 4->5 WITH DUPLICATION: does division of labor emerge in a real population? ===\n");
	printf("  (rung45 without duplication stalled: spec frac stayed 0. Here specialization ADDS, not displaces.)\n\n");
	for(int c=0;c<NCOMP;c++){ n[c][0]=6; for(int k=1;k<=F;k++) n[c][k]=0; }   /* all generalist, size 6 */
	RNG=0x9e3779b97f4a7c15ULL;
	double w[F+1]; w[0]=1.0; for(int k=1;k<=F;k++) w[k]=1.0+SSPEC;

	printf("  %-5s %10s %13s %9s %9s\n","gen","spec frac","all-F present","mean sz","viab");
	for(int g=0;g<=GEN;g++){
		if(g%50==0||g==GEN){
			long tot=0,spec=0; int allF=0; double msz=0,mv=0;
			for(int c=0;c<NCOMP;c++){ int s=sizeof_(n[c]); tot+=s; for(int k=1;k<=F;k++) spec+=n[c][k];
				int have=0; for(int k=1;k<=F;k++) if(n[c][k]>0) have++; if(have==F) allF++;
				msz+=s; mv+=viab(n[c]); }
			printf("  %-5d %10.3f %12.1f%% %9.1f %9.2f\n",g,(double)spec/tot,100.0*allF/NCOMP,msz/NCOMP,mv/NCOMP);
		}
		if(g==GEN) break;
		for(int c=0;c<NCOMP;c++){
			int m[F+1]; for(int k=0;k<=F;k++) m[k]=n[c][k];
			int s=sizeof_(m), target=2*s; if(target>DMAX) target=DMAX;
			for(int i=s;i<target;i++){
				double Z=0; for(int k=0;k<=F;k++) Z+=m[k]*w[k]; if(Z<1e-12)break;
				double u=urand()*Z,acc=0; int r=0; for(;r<=F;r++){ acc+=m[r]*w[r]; if(u<=acc) break; } if(r>F)r=F;
				int off=r;
				if(r==0){ if(urand()<MUDUP) off=1+(int)(xs()%F); }       /* duplicate + diverge */
				else    { if(urand()<MULOSS) off=0; }                    /* revert */
				m[off]++;
			}
			for(int k=0;k<=F;k++){ int d1=0; for(int i=0;i<m[k];i++) if(urand()<0.5) d1++; dau[2*c][k]=d1; dau[2*c+1][k]=m[k]-d1; }
		}
		double tot=0;
		for(int i=0;i<2*NCOMP;i++){ int sz=sizeof_(dau[i]); double weight=viab(dau[i])/(1.0+SIZECOST*sz); if(sz<1)weight=0; tot+=weight; cum[i]=tot; }
		if(tot<=0){ for(int c=0;c<NCOMP;c++){ n[c][0]=1; for(int k=1;k<=F;k++) n[c][k]=0; } }
		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; }
			for(int k=0;k<=F;k++) n[c][k]=dau[lo][k]; }
	}
	printf("\n  High spec frac + high all-F + viability up = division of labor EMERGED (rung 5) in a real\n");
	printf("  population — bridged by duplication (the rung-2 copying capacity repurposed). Low = still stalled.\n");
	return 0;
}