OverviewFilesCommitsIssuesDocsReleasesPeers

basal-agent / gard.c

96 lines · c ref:
/* gard.c — does HEREDITY emerge from an autocatalytic set? (rung 1 -> rung 2 as emergence)
 *
 * NO replicator, NO templating, NO heredity is coded. Given: a random catalytic chemistry
 * (molecules catalyze each other's entry into an assembly) + a splitting rule. Observed:
 * whether a lineage's COMPOSITION is transmitted across splits on its own (compositional
 * inheritance = the composome/GARD idea, Segre-Lancet).
 *
 * Mechanism that would MAKE it emergent (not designed): if the catalytic dynamics have
 * multiple attractor compositions ("composomes"), then a split perturbs the composition but
 * autocatalytic REGROWTH pulls the daughter back to the parent's attractor -> the composition
 * breeds true. Heredity without a genome.
 *
 * Strict test (no answer encoded):
 *   1. how many DISTINCT mature compositions do random founders fall into? (>1 = heritable variety)
 *   2. after split + regrow, is cosine(parent, daughter) >> cosine(parent, random assembly)?
 *      (composition transmitted through the split = heredity EMERGED)
 * Honest prior: heredity may emerge but be LIMITED (Vasas-Szathmary: composomes low-evolvability).
 * Report whatever happens, including a null.
 *
 * build:  cc -O2 -Wall -std=c11 -o gard gard.c -lm
 */
#include <math.h>
#include <stdio.h>
#include <string.h>

#define N 40            /* molecule types */
#define S 200           /* assembly size (molecules) */
#define MAT 6000        /* maturation steps (join/leave to reach attractor) */
#define PCAT 0.15       /* catalytic-network density */
#define RHO0 0.15       /* weaker basal -> autocatalysis dominates -> sharper, multiple attractors */

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 double randn(void){ double u=urand(),v=urand(); if(u<1e-300)u=1e-300; return sqrt(-2*log(u))*cos(6.283185307*v); }

static double beta[N][N];       /* beta[i][j] = catalysis of type-i entry by present type j */
static double rate[N];          /* rate[i] = RHO0 + sum_j beta[i][j]*n[j], maintained incrementally */

static void build_chem(void){   /* the GIVEN random chemistry */
	for(int i=0;i<N;i++) for(int j=0;j<N;j++) beta[i][j]=(urand()<PCAT)? exp(2.5*randn())*0.30 : 0.0;
}
static void recompute_rate(const int*n){ for(int i=0;i<N;i++){ double r=RHO0; for(int j=0;j<N;j++) r+=beta[i][j]*n[j]; rate[i]=r; } }
static int pick_add(void){ double tot=0; for(int i=0;i<N;i++) tot+=rate[i]; double u=urand()*tot,a=0; for(int i=0;i<N;i++){ a+=rate[i]; if(u<=a) return i; } return N-1; }
static int pick_rem(const int*n,int size){ double u=urand()*size,a=0; for(int j=0;j<N;j++){ a+=n[j]; if(u<=a) return j; } return N-1; }
static void addmol(int*n,int i){ n[i]++; for(int k=0;k<N;k++) rate[k]+=beta[k][i]; }
static void remmol(int*n,int j){ n[j]--; for(int k=0;k<N;k++) rate[k]-=beta[k][j]; }

/* mature an assembly of given size to its attractor composition (constant-size join/leave) */
static void mature(int*n,int size){ recompute_rate(n);
	for(int t=0;t<MAT;t++){ int j=pick_rem(n,size); remmol(n,j); int i=pick_add(); addmol(n,i); }
}
static void grow_to(int*n,int*size,int target){ recompute_rate(n);
	while(*size<target){ int i=pick_add(); addmol(n,i); (*size)++; }
}
static double cosine(const int*a,const int*b){ double d=0,na=0,nb=0; for(int i=0;i<N;i++){ d+=(double)a[i]*b[i]; na+=(double)a[i]*a[i]; nb+=(double)b[i]*b[i]; } if(na<=0||nb<=0)return 0; return d/(sqrt(na)*sqrt(nb)); }

int main(void){
	RNG=0x9e3779b97f4a7c15ULL; build_chem();

	/* --- founders: mature random assemblies, count distinct composomes --- */
	#define K 50
	static int fnd[K][N];
	for(int k=0;k<K;k++){ int n[N]; memset(n,0,sizeof n); for(int s=0;s<S;s++){ n[(int)(xs()%N)]++; } mature(n,S); memcpy(fnd[k],n,sizeof n); }
	int nclust=0; int rep[K];
	for(int k=0;k<K;k++){ int found=0; for(int c=0;c<nclust;c++) if(cosine(fnd[k],fnd[rep[c]])>0.85){found=1;break;} if(!found) rep[nclust++]=k; }

	/* --- heredity test: split a matured parent, regrow the daughter, compare compositions --- */
	double sum_pd=0,sum_pr=0; int trials=200;
	for(int tr=0;tr<trials;tr++){
		int P[N]; memset(P,0,sizeof P); for(int s=0;s<S;s++) P[(int)(xs()%N)]++; mature(P,S);
		int D[N]; memset(D,0,sizeof D); int ds=0;                     /* split: each molecule -> daughter w.p. 0.5 */
		for(int i=0;i<N;i++) for(int c=0;c<P[i];c++) if(urand()<0.5){ D[i]++; ds++; }
		if(ds<1){ D[0]=1; ds=1; }
		grow_to(D,&ds,S); mature(D,S);                                /* autocatalytic regrowth + maturation */
		int R[N]; memcpy(R,fnd[(int)(xs()%K)],sizeof R);              /* a random matured assembly */
		sum_pd += cosine(P,D); sum_pr += cosine(P,R);
	}
	double pd=sum_pd/trials, pr=sum_pr/trials;

	printf("=== HEREDITY FROM AN AUTOCATALYTIC SET (rung1 -> rung2, emergence test) ===\n");
	printf("  chemistry: %d molecule types, random catalytic network (density %.2f). NOTHING about\n",N,PCAT);
	printf("  heredity/replication is coded — only chemistry + splitting.\n\n");
	printf("  distinct composomes among %d random founders : %d\n",K,nclust);
	printf("  parent<->daughter composition similarity      : %.3f  (after split + autocatalytic regrow)\n",pd);
	printf("  parent<->random   composition similarity      : %.3f  (baseline)\n",pr);
	printf("  transmission signal (pd - pr)                 : %+.3f\n\n",pd-pr);
	if(pd-pr>0.15 && nclust>1)
		printf("  => HEREDITY EMERGED: composition is transmitted across splits (daughters resemble\n     parents far more than random), with %d heritable composome types. No genome coded.\n",nclust);
	else if(pd-pr>0.05)
		printf("  => WEAK/LIMITED heredity: some transmission, but composomes are few/blurred\n     (the Vasas-Szathmary limit). Honest partial result.\n");
	else
		printf("  => NO heredity emerged: daughters no more like parents than random. Autocatalysis\n     alone did not transmit composition here. Honest null.\n");
	return 0;
}