| … | … | @@ -0,0 +1,166 @@ |
| 1 | /* container.c — MILESTONE 2: containers as species. And a structural check that may kill the design. |
| 2 | * |
| 3 | * substrate.c passed milestone 1: one type expressing rung 1 (a self-sustaining reaction subset) and |
| 4 | * rung 2 (a reaction whose product catalyses itself), coexisting in one network with no translation. |
| 5 | * The next move is the recursive one — a CONTAINER IS ITSELF A SPECIES, so a compartment can be |
| 6 | * produced, consumed, catalysed and nested like anything else. That is what would let a rung's |
| 7 | * output be the next rung's input. |
| 8 | * |
| 9 | * THE CHECK THAT COMES FIRST, AND MIGHT KILL IT. Before building on containers, ask whether this |
| 10 | * type can express what rung 4 is FOR. In ribozyme.c compartments bought privatisation against |
| 11 | * parasites: a catalyst-rich compartment out-reproduces a parasite-ridden one. That is a claim about |
| 12 | * relative ABUNDANCE. But substrate.c's RAF is a purely STRUCTURAL notion — which reactions can |
| 13 | * sustain themselves given which species are present — and RAF is MONOTONE in the reaction set: |
| 14 | * adding reactions can never remove closure. If so, a container can never hold a RAF that the whole |
| 15 | * mixture lacks, and compartmentalisation cannot buy anything expressible in this type. |
| 16 | * |
| 17 | * That is a falsifiable structural prediction about the substrate's own design, so it is tested here |
| 18 | * rather than assumed: |
| 19 | * C1 containers work at all — a container's RAF is computed over ITS OWN contents (locality is |
| 20 | * what a compartment IS), containers can be reaction products, and containers can nest. |
| 21 | * C2 THE KILLER: is global RAF always a superset of every container's RAF? Measured over many |
| 22 | * random networks and random partitions. If it always is, static closure cannot express rung 4 |
| 23 | * and the type needs ABUNDANCES, not just presence. |
| 24 | * |
| 25 | * A negative on C2 is the useful outcome: it says precisely what milestone 3 must add. |
| 26 | * |
| 27 | * build: cc -O2 -Wall -std=c11 -o container container.c -lm |
| 28 | */ |
| 29 | #include <stdio.h> |
| 30 | #include <string.h> |
| 31 | |
| 32 | #define NSPEC 48 |
| 33 | #define NFOOD 6 |
| 34 | #define NRXN 200 |
| 35 | #define MAXC 8 /* container species occupy the top MAXC species indices */ |
| 36 | |
| 37 | static unsigned long long RNG; |
| 38 | static unsigned long long xs(void){ RNG^=RNG<<13; RNG^=RNG>>7; RNG^=RNG<<17; return RNG; } |
| 39 | static double urand(void){ return (double)(xs()>>11)*(1.0/9007199254740992.0); } |
| 40 | static int rint_(int n){ return (int)(xs()%(unsigned)n); } |
| 41 | |
| 42 | typedef struct { int a,b,prod; unsigned char cat[NSPEC]; } Rxn; |
| 43 | static Rxn R[NRXN]; static int nrxn; |
| 44 | |
| 45 | /* ---- THE RECURSIVE MOVE: a container is a species that also has CONTENTS. Species index c is a |
| 46 | container iff c >= NSPEC-MAXC. Its contents are a set of species — which may include other |
| 47 | containers, so nesting is free. ---- */ |
| 48 | static unsigned char contents[MAXC][NSPEC]; |
| 49 | static int is_container(int s){ return s>=NSPEC-MAXC; } |
| 50 | static int cidx(int s){ return s-(NSPEC-MAXC); } |
| 51 | |
| 52 | static unsigned char present[NSPEC], active[NRXN]; |
| 53 | |
| 54 | /* RAF over a RESTRICTED species scope: only species in `scope` may be used. Passing an all-ones |
| 55 | scope gives the global RAF; passing a container's contents gives that container's local RAF. |
| 56 | Locality is the whole of what a compartment is, in this type. */ |
| 57 | static int raf_scope(const unsigned char *scope){ |
| 58 | for(int j=0;j<nrxn;j++) active[j]=1; |
| 59 | for(;;){ |
| 60 | for(int m=0;m<NSPEC;m++) present[m]=(m<NFOOD && scope[m])?1:0; |
| 61 | int grew=1; |
| 62 | while(grew){ grew=0; |
| 63 | for(int j=0;j<nrxn;j++){ if(!active[j]) continue; |
| 64 | int a=R[j].a,b=R[j].b,p=R[j].prod; |
| 65 | if(!scope[a]||!scope[b]||!scope[p]) continue; |
| 66 | if(!present[a]||!present[b]||present[p]) continue; |
| 67 | int cz=0; for(int m=0;m<NSPEC;m++) if(R[j].cat[m]&&scope[m]&&present[m]){cz=1;break;} |
| 68 | if(cz){ present[p]=1; grew=1; } |
| 69 | } |
| 70 | } |
| 71 | int ch=0; |
| 72 | for(int j=0;j<nrxn;j++){ if(!active[j]) continue; |
| 73 | int ok = scope[R[j].a]&&scope[R[j].b]&&scope[R[j].prod] |
| 74 | && present[R[j].a]&&present[R[j].b]; |
| 75 | int cz=0; if(ok){ for(int m=0;m<NSPEC;m++) if(R[j].cat[m]&&scope[m]&&present[m]){cz=1;break;} } |
| 76 | if(!(ok&&cz)){ active[j]=0; ch=1; } |
| 77 | } |
| 78 | if(!ch) break; |
| 79 | } |
| 80 | int n=0; for(int j=0;j<nrxn;j++) n+=active[j]; |
| 81 | return n; |
| 82 | } |
| 83 | static void raf_mark(const unsigned char *scope,unsigned char *out){ |
| 84 | raf_scope(scope); memcpy(out,active,nrxn); |
| 85 | } |
| 86 | |
| 87 | static void net_random(double p){ |
| 88 | nrxn=0; |
| 89 | for(int j=0;j<NRXN;j++){ |
| 90 | R[j].a=rint_(NSPEC); R[j].b=rint_(NSPEC); R[j].prod=NFOOD+rint_(NSPEC-NFOOD); |
| 91 | memset(R[j].cat,0,sizeof R[j].cat); |
| 92 | for(int m=0;m<NSPEC;m++) if(urand()<p) R[j].cat[m]=1; |
| 93 | nrxn++; |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | int main(void) |
| 98 | { |
| 99 | unsigned char all[NSPEC], sc[NSPEC]; |
| 100 | for(int m=0;m<NSPEC;m++) all[m]=1; |
| 101 | |
| 102 | printf("=== MILESTONE 2: containers as species — and the check that may kill the design ===\n"); |
| 103 | printf(" A container is a species that also has contents; contents may include containers, so\n"); |
| 104 | printf(" nesting is free. A container's RAF is its contents' RAF — locality is what a\n"); |
| 105 | printf(" compartment IS. %d species (%d food, top %d are containers), %d reactions.\n\n",NSPEC,NFOOD,MAXC,NRXN); |
| 106 | |
| 107 | /* ---- C1: containers exist, are products, and nest ---- */ |
| 108 | printf(" C1 — containers work at all\n"); |
| 109 | RNG=0x9e3779b97f4a7c15ULL; net_random(0.030); |
| 110 | int K1=NSPEC-MAXC, K2=NSPEC-MAXC+1; |
| 111 | memset(contents,0,sizeof contents); |
| 112 | for(int m=0;m<NFOOD;m++){ contents[cidx(K1)][m]=1; contents[cidx(K2)][m]=1; } |
| 113 | for(int m=NFOOD;m<NFOOD+12;m++) contents[cidx(K1)][m]=1; |
| 114 | contents[cidx(K2)][K1]=1; /* K2 CONTAINS K1 — nesting */ |
| 115 | R[0].a=0; R[0].b=1; R[0].prod=K1; R[0].cat[0]=1;/* a reaction whose PRODUCT is a container */ |
| 116 | int prod_ok = (R[0].prod==K1) && is_container(R[0].prod); |
| 117 | int nest_ok = contents[cidx(K2)][K1] && is_container(K1); |
| 118 | memcpy(sc,contents[cidx(K1)],NSPEC); int local=raf_scope(sc); |
| 119 | int glob=raf_scope(all); |
| 120 | printf(" container is a reaction product : %s\n",prod_ok?"YES":"no"); |
| 121 | printf(" container nested in a container : %s\n",nest_ok?"YES":"no"); |
| 122 | printf(" local RAF (inside K1) %d vs global RAF %d\n",local,glob); |
| 123 | int c1 = prod_ok && nest_ok; |
| 124 | printf(" => %s\n\n", c1?"containers are ordinary species — the recursive move works" |
| 125 | :"containers are NOT ordinary species here — fix before proceeding"); |
| 126 | |
| 127 | /* ---- C2: is a container's RAF always a SUBSET of the global RAF? ---- */ |
| 128 | printf(" C2 — THE KILLER. Can a container hold closure the whole mixture lacks?\n"); |
| 129 | printf(" Random networks x random partitions; count any reaction in a container's RAF\n"); |
| 130 | printf(" but NOT in the global RAF.\n"); |
| 131 | int trials=0, violations=0, nonempty=0; |
| 132 | static unsigned char gmark[NRXN], lmark[NRXN]; |
| 133 | for(int t=0;t<400;t++){ |
| 134 | RNG=0x2545f4914f6cdd1dULL + (unsigned long long)t*0x9E3779B97F4A7C15ULL; |
| 135 | net_random(0.020+0.02*urand()); |
| 136 | raf_mark(all,gmark); |
| 137 | for(int r=0;r<3;r++){ |
| 138 | for(int m=0;m<NSPEC;m++) sc[m]=(m<NFOOD)?1:(urand()<0.5); |
| 139 | raf_mark(sc,lmark); |
| 140 | int loc=0,viol=0; |
| 141 | for(int j=0;j<nrxn;j++){ loc+=lmark[j]; if(lmark[j]&&!gmark[j]) viol++; } |
| 142 | trials++; if(loc>0) nonempty++; if(viol>0) violations++; |
| 143 | } |
| 144 | } |
| 145 | printf(" %d partitions, %d with a non-empty local RAF, %d holding closure the global\n",trials,nonempty,violations); |
| 146 | printf(" mixture lacks.\n"); |
| 147 | printf(" => %s\n\n", violations==0? |
| 148 | "NONE. RAF is MONOTONE: a container can never hold closure the mixture lacks." |
| 149 | :"some containers DO hold closure the mixture lacks — the design survives."); |
| 150 | |
| 151 | if(violations==0) |
| 152 | printf(" MILESTONE 2: CONTAINERS WORK, BUT STATIC CLOSURE CANNOT EXPRESS RUNG 4.\n" |
| 153 | " The recursive move is sound — containers are ordinary species, they can be produced\n" |
| 154 | " and nested. But compartmentalisation buys NOTHING expressible in a presence/absence\n" |
| 155 | " type, because RAF is monotone in the reaction set: adding reactions never removes\n" |
| 156 | " closure, so a part can never sustain what the whole cannot. What ribozyme.c's\n" |
| 157 | " compartments actually bought was PRIVATISATION AGAINST PARASITES — a claim about\n" |
| 158 | " relative ABUNDANCE, not about what can exist. So milestone 3 is not more structure:\n" |
| 159 | " it is COUNTS. The type must carry how MUCH of each species is present, and the RAF\n" |
| 160 | " becomes a dynamical attractor rather than a static closure. That is a concrete,\n" |
| 161 | " measured design requirement rather than a guess — and it is the honest reason the\n" |
| 162 | " substrate is not yet ready to chain rung 3 onward.\n"); |
| 163 | else |
| 164 | printf(" MILESTONE 2 PASSED as designed — proceed to expressing rungs 3 and 5.\n"); |
| 165 | return 0; |
| 166 | } |