OverviewFilesCommitsIssuesDocsReleasesPeers

basal-agent / container.c

167 lines · c ref:
/* container.c — MILESTONE 2: containers as species. And a structural check that may kill the design.
 *
 * substrate.c passed milestone 1: one type expressing rung 1 (a self-sustaining reaction subset) and
 * rung 2 (a reaction whose product catalyses itself), coexisting in one network with no translation.
 * The next move is the recursive one — a CONTAINER IS ITSELF A SPECIES, so a compartment can be
 * produced, consumed, catalysed and nested like anything else. That is what would let a rung's
 * output be the next rung's input.
 *
 * THE CHECK THAT COMES FIRST, AND MIGHT KILL IT. Before building on containers, ask whether this
 * type can express what rung 4 is FOR. In ribozyme.c compartments bought privatisation against
 * parasites: a catalyst-rich compartment out-reproduces a parasite-ridden one. That is a claim about
 * relative ABUNDANCE. But substrate.c's RAF is a purely STRUCTURAL notion — which reactions can
 * sustain themselves given which species are present — and RAF is MONOTONE in the reaction set:
 * adding reactions can never remove closure. If so, a container can never hold a RAF that the whole
 * mixture lacks, and compartmentalisation cannot buy anything expressible in this type.
 *
 * That is a falsifiable structural prediction about the substrate's own design, so it is tested here
 * rather than assumed:
 *   C1 containers work at all — a container's RAF is computed over ITS OWN contents (locality is
 *      what a compartment IS), containers can be reaction products, and containers can nest.
 *   C2 THE KILLER: is global RAF always a superset of every container's RAF? Measured over many
 *      random networks and random partitions. If it always is, static closure cannot express rung 4
 *      and the type needs ABUNDANCES, not just presence.
 *
 * A negative on C2 is the useful outcome: it says precisely what milestone 3 must add.
 *
 * build:  cc -O2 -Wall -std=c11 -o container container.c -lm
 */
#include <stdio.h>
#include <string.h>

#define NSPEC 48
#define NFOOD 6
#define NRXN  200
#define MAXC  8            /* container species occupy the top MAXC species indices */

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 rint_(int n){ return (int)(xs()%(unsigned)n); }

typedef struct { int a,b,prod; unsigned char cat[NSPEC]; } Rxn;
static Rxn R[NRXN]; static int nrxn;

/* ---- THE RECURSIVE MOVE: a container is a species that also has CONTENTS. Species index c is a
   container iff c >= NSPEC-MAXC. Its contents are a set of species — which may include other
   containers, so nesting is free. ---- */
static unsigned char contents[MAXC][NSPEC];
static int is_container(int s){ return s>=NSPEC-MAXC; }
static int cidx(int s){ return s-(NSPEC-MAXC); }

static unsigned char present[NSPEC], active[NRXN];

/* RAF over a RESTRICTED species scope: only species in `scope` may be used. Passing an all-ones
   scope gives the global RAF; passing a container's contents gives that container's local RAF.
   Locality is the whole of what a compartment is, in this type. */
static int raf_scope(const unsigned char *scope){
	for(int j=0;j<nrxn;j++) active[j]=1;
	for(;;){
		for(int m=0;m<NSPEC;m++) present[m]=(m<NFOOD && scope[m])?1:0;
		int grew=1;
		while(grew){ grew=0;
			for(int j=0;j<nrxn;j++){ if(!active[j]) continue;
				int a=R[j].a,b=R[j].b,p=R[j].prod;
				if(!scope[a]||!scope[b]||!scope[p]) continue;
				if(!present[a]||!present[b]||present[p]) continue;
				int cz=0; for(int m=0;m<NSPEC;m++) if(R[j].cat[m]&&scope[m]&&present[m]){cz=1;break;}
				if(cz){ present[p]=1; grew=1; }
			}
		}
		int ch=0;
		for(int j=0;j<nrxn;j++){ if(!active[j]) continue;
			int ok = scope[R[j].a]&&scope[R[j].b]&&scope[R[j].prod]
			      && present[R[j].a]&&present[R[j].b];
			int cz=0; if(ok){ for(int m=0;m<NSPEC;m++) if(R[j].cat[m]&&scope[m]&&present[m]){cz=1;break;} }
			if(!(ok&&cz)){ active[j]=0; ch=1; }
		}
		if(!ch) break;
	}
	int n=0; for(int j=0;j<nrxn;j++) n+=active[j];
	return n;
}
static void raf_mark(const unsigned char *scope,unsigned char *out){
	raf_scope(scope); memcpy(out,active,nrxn);
}

static void net_random(double p){
	nrxn=0;
	for(int j=0;j<NRXN;j++){
		R[j].a=rint_(NSPEC); R[j].b=rint_(NSPEC); R[j].prod=NFOOD+rint_(NSPEC-NFOOD);
		memset(R[j].cat,0,sizeof R[j].cat);
		for(int m=0;m<NSPEC;m++) if(urand()<p) R[j].cat[m]=1;
		nrxn++;
	}
}

int main(void)
{
	unsigned char all[NSPEC], sc[NSPEC];
	for(int m=0;m<NSPEC;m++) all[m]=1;

	printf("=== MILESTONE 2: containers as species — and the check that may kill the design ===\n");
	printf("  A container is a species that also has contents; contents may include containers, so\n");
	printf("  nesting is free. A container's RAF is its contents' RAF — locality is what a\n");
	printf("  compartment IS. %d species (%d food, top %d are containers), %d reactions.\n\n",NSPEC,NFOOD,MAXC,NRXN);

	/* ---- C1: containers exist, are products, and nest ---- */
	printf("  C1 — containers work at all\n");
	RNG=0x9e3779b97f4a7c15ULL; net_random(0.030);
	int K1=NSPEC-MAXC, K2=NSPEC-MAXC+1;
	memset(contents,0,sizeof contents);
	for(int m=0;m<NFOOD;m++){ contents[cidx(K1)][m]=1; contents[cidx(K2)][m]=1; }
	for(int m=NFOOD;m<NFOOD+12;m++) contents[cidx(K1)][m]=1;
	contents[cidx(K2)][K1]=1;                       /* K2 CONTAINS K1 — nesting */
	R[0].a=0; R[0].b=1; R[0].prod=K1; R[0].cat[0]=1;/* a reaction whose PRODUCT is a container */
	int prod_ok = (R[0].prod==K1) && is_container(R[0].prod);
	int nest_ok = contents[cidx(K2)][K1] && is_container(K1);
	memcpy(sc,contents[cidx(K1)],NSPEC); int local=raf_scope(sc);
	int glob=raf_scope(all);
	printf("     container is a reaction product : %s\n",prod_ok?"YES":"no");
	printf("     container nested in a container : %s\n",nest_ok?"YES":"no");
	printf("     local RAF (inside K1) %d   vs global RAF %d\n",local,glob);
	int c1 = prod_ok && nest_ok;
	printf("     => %s\n\n", c1?"containers are ordinary species — the recursive move works"
	                          :"containers are NOT ordinary species here — fix before proceeding");

	/* ---- C2: is a container's RAF always a SUBSET of the global RAF? ---- */
	printf("  C2 — THE KILLER. Can a container hold closure the whole mixture lacks?\n");
	printf("       Random networks x random partitions; count any reaction in a container's RAF\n");
	printf("       but NOT in the global RAF.\n");
	int trials=0, violations=0, nonempty=0;
	static unsigned char gmark[NRXN], lmark[NRXN];
	for(int t=0;t<400;t++){
		RNG=0x2545f4914f6cdd1dULL + (unsigned long long)t*0x9E3779B97F4A7C15ULL;
		net_random(0.020+0.02*urand());
		raf_mark(all,gmark);
		for(int r=0;r<3;r++){
			for(int m=0;m<NSPEC;m++) sc[m]=(m<NFOOD)?1:(urand()<0.5);
			raf_mark(sc,lmark);
			int loc=0,viol=0;
			for(int j=0;j<nrxn;j++){ loc+=lmark[j]; if(lmark[j]&&!gmark[j]) viol++; }
			trials++; if(loc>0) nonempty++; if(viol>0) violations++;
		}
	}
	printf("       %d partitions, %d with a non-empty local RAF, %d holding closure the global\n",trials,nonempty,violations);
	printf("       mixture lacks.\n");
	printf("     => %s\n\n", violations==0?
		"NONE. RAF is MONOTONE: a container can never hold closure the mixture lacks."
		:"some containers DO hold closure the mixture lacks — the design survives.");

	if(violations==0)
		printf("  MILESTONE 2: CONTAINERS WORK, BUT STATIC CLOSURE CANNOT EXPRESS RUNG 4.\n"
		       "  The recursive move is sound — containers are ordinary species, they can be produced\n"
		       "  and nested. But compartmentalisation buys NOTHING expressible in a presence/absence\n"
		       "  type, because RAF is monotone in the reaction set: adding reactions never removes\n"
		       "  closure, so a part can never sustain what the whole cannot. What ribozyme.c's\n"
		       "  compartments actually bought was PRIVATISATION AGAINST PARASITES — a claim about\n"
		       "  relative ABUNDANCE, not about what can exist. So milestone 3 is not more structure:\n"
		       "  it is COUNTS. The type must carry how MUCH of each species is present, and the RAF\n"
		       "  becomes a dynamical attractor rather than a static closure. That is a concrete,\n"
		       "  measured design requirement rather than a guess — and it is the honest reason the\n"
		       "  substrate is not yet ready to chain rung 3 onward.\n");
	else
		printf("  MILESTONE 2 PASSED as designed — proceed to expressing rungs 3 and 5.\n");
	return 0;
}