OverviewFilesCommitsIssuesDocsReleasesPeers

basal-agent / rung3.c

76 lines · c ref:
/* rung3.c — the sequence replicator: the SHARP error catastrophe, and the ignition
 * of open-ended evolution. Built on rung 2 (templating) but forms are now SEQUENCES.
 *
 * Rung 2's fidelity erosion was SMOOTH (few forms). Make the form a length-L sequence
 * (2^L of them) with a single fittest "master" sequence (fitness sigma, all others 1),
 * and rung 2's soft requirement becomes Eigen's SHARP ERROR CATASTROPHE — a real phase
 * transition at a critical per-site mutation rate mu_c ~ ln(sigma)/L:
 *   mu < mu_c : population LOCALIZES on the master (heredity of a large information store
 *               = the genome; the stable substrate the "directed read" needs)
 *   mu > mu_c : population DELOCALIZES over sequence space (information lost)
 *
 * Exact deterministic quasispecies over Hamming-distance classes k=0..L (fitness and
 * mutation are permutation-symmetric, so classes suffice). Class transition is a
 * binomial convolution: a parent at distance j has j "wrong" and L-j "right" sites;
 * offspring distance i = j - (corrected) + (newly wrong).
 *
 * This is the rung where heredity+variation+selection over a vast form-space becomes
 * open-ended evolution — the point from which the ladder builds ITSELF (rung 3, stabilized
 * by rung 4). Contrast the SHARP drop here with rung 2's smooth curve.
 *
 * build:  cc -O2 -Wall -std=c11 -o rung3 rung3.c -lm
 */
#include <math.h>
#include <stdio.h>

#define L 20                     /* sequence length -> 2^L forms */
#define SIGMA 10.0               /* master-sequence fitness advantage */

static double binom[L+1][L+1];
static void init_binom(void){
	for(int n=0;n<=L;n++){ binom[n][0]=1; for(int k=1;k<=n;k++) binom[n][k]=binom[n-1][k-1]+(k<=n-1?binom[n-1][k]:0); }
}

/* T[i][j] = P(offspring in class i | parent in class j), per-site mutation mu */
static void build_T(double T[L+1][L+1],double mu){
	for(int i=0;i<=L;i++)for(int j=0;j<=L;j++) T[i][j]=0;
	for(int j=0;j<=L;j++){
		for(int c=0;c<=j;c++){                       /* c of the j wrong sites corrected */
			double pc=binom[j][c]*pow(mu,c)*pow(1-mu,j-c);
			for(int w=0;w<=L-j;w++){                  /* w of the L-j right sites newly wrong */
				double pw=binom[L-j][w]*pow(mu,w)*pow(1-mu,L-j-w);
				int i=j-c+w; T[i][j]+=pc*pw;
			}
		}
	}
}

int main(void){
	init_binom();
	double f[L+1]; f[0]=SIGMA; for(int k=1;k<=L;k++) f[k]=1.0;
	double mu_c=log(SIGMA)/L;

	printf("=== RUNG 3: SEQUENCE REPLICATOR — the SHARP error catastrophe ===\n");
	printf("  L=%d (2^%d forms), master fitness sigma=%.0f; predicted mu_c ~ ln(sigma)/L = %.4f\n\n",L,L,SIGMA,mu_c);
	printf("  %-7s %12s %12s\n","mu","master frac","mean dist");
	for(int mi=0;mi<=24;mi++){
		double mu=mi*0.01;
		double T[L+1][L+1]; build_T(T,mu);
		double y[L+1]; for(int k=0;k<=L;k++) y[k]=1.0/(L+1);
		for(int it=0;it<4000;it++){
			double ny[L+1],tot=0;
			for(int i=0;i<=L;i++){ ny[i]=0; for(int j=0;j<=L;j++) ny[i]+=T[i][j]*f[j]*y[j]; tot+=ny[i]; }
			if(tot<1e-300)tot=1e-300;
			for(int i=0;i<=L;i++) y[i]=ny[i]/tot;
		}
		double md=0; for(int k=0;k<=L;k++) md+=k*y[k];
		int bar=(int)(y[0]*40+0.5); char b[64]; for(int k=0;k<bar&&k<50;k++)b[k]='#'; b[bar<50?bar:50]=0;
		printf("  %-7.2f %12.3f %12.2f  %s%s\n",mu,y[0],md,b,(mi>0 && fabs(mu-mu_c)<0.005)?"  <- mu_c":"");
	}
	printf("\n  SHARP transition near mu_c=%.3f: below it the population localizes on the master\n",mu_c);
	printf("  (a length-%d information store is inherited); above it, delocalization (mean dist\n",L);
	printf("  -> L/2=%.0f, information lost). This is the error catastrophe — the sharp version of\n",L/2.0);
	printf("  rung 2's smooth curve, and the substrate on which open-ended evolution ignites.\n");
	return 0;
}