| … | … | @@ -0,0 +1,171 @@ |
| 1 | /* counts.c — MILESTONE 3: abundances. Can the substrate now express what rung 4 is FOR? |
| 2 | * |
| 3 | * container.c settled milestone 2 with a decisive structural negative: containers ARE ordinary |
| 4 | * species (products, nestable, local scope) but RAF is MONOTONE in the reaction set, so across 1200 |
| 5 | * partitions ZERO containers held closure the whole mixture lacked. A presence/absence type cannot |
| 6 | * express compartmentalisation, because what compartments buy is privatisation against parasites — |
| 7 | * a claim about relative ABUNDANCE, not about what can exist. |
| 8 | * |
| 9 | * So the type gains counts. A container holds n[species] rather than a bit, reactions consume and |
| 10 | * produce molecules, and the "RAF" becomes a dynamical attractor: which species are MAINTAINED, |
| 11 | * not which could exist. |
| 12 | * |
| 13 | * PARASITES ARE NOT ADDED — they fall out. In a random network some species catalyse reactions and |
| 14 | * some catalyse nothing. The latter still get produced and still occupy capacity. That is exactly |
| 15 | * ribozyme.c's parasite, arising from the network rather than being declared. |
| 16 | * |
| 17 | * THE CHECK, and it is the same discipline as milestone 1: a new type is worthless if it does not |
| 18 | * reproduce what we already have. |
| 19 | * M1 the dynamics sustain anything at all — catalyst fraction must not simply collapse to 0 or |
| 20 | * saturate at 1 in every arm, or there is nothing to compare. |
| 21 | * M2 THE TEST: does WELL-MIXED lose to COMPARTMENTS + between-compartment selection, as |
| 22 | * ribozyme.c found (5.99 -> 5.50 well-mixed vs -> 11.34 at compartment size 8)? If the new |
| 23 | * type reproduces that ordering, it can express rung 4 and the substrate is ready to chain. |
| 24 | * If compartments do NOT win, counts were not the missing piece either. |
| 25 | * M3 CONTROL: compartments with selection REMOVED (reseed from a random parent rather than a |
| 26 | * catalyst-rich one). Isolates compartment-level selection from compartmentalisation per se — |
| 27 | * the distinction byproduct.c had to learn the hard way. |
| 28 | * |
| 29 | * Total molecules are held EQUAL across arms so the comparison is not confounded by population size, |
| 30 | * per the four-check tally. |
| 31 | * |
| 32 | * build: cc -O2 -Wall -std=c11 -o counts counts.c -lm |
| 33 | */ |
| 34 | #include <stdio.h> |
| 35 | #include <string.h> |
| 36 | |
| 37 | #define NSPEC 40 |
| 38 | #define NFOOD 6 |
| 39 | #define NRXN 160 |
| 40 | #define TOTAL 4800 /* molecules, identical in every arm */ |
| 41 | #define STEPS 300 |
| 42 | #define PCAT 0.004 |
| 43 | |
| 44 | static unsigned long long RNG; |
| 45 | static unsigned long long xs(void){ RNG^=RNG<<13; RNG^=RNG>>7; RNG^=RNG<<17; return RNG; } |
| 46 | static double urand(void){ return (double)(xs()>>11)*(1.0/9007199254740992.0); } |
| 47 | static int rint_(int n){ return (int)(xs()%(unsigned)n); } |
| 48 | |
| 49 | typedef struct { int a,b,prod; unsigned char cat[NSPEC]; } Rxn; |
| 50 | static Rxn R[NRXN]; |
| 51 | static unsigned char catalyses[NSPEC]; /* does this species catalyse ANY reaction? */ |
| 52 | |
| 53 | static void net_random(void){ |
| 54 | memset(catalyses,0,sizeof catalyses); |
| 55 | for(int j=0;j<NRXN;j++){ |
| 56 | R[j].a=rint_(NSPEC); R[j].b=rint_(NSPEC); R[j].prod=NFOOD+rint_(NSPEC-NFOOD); |
| 57 | memset(R[j].cat,0,sizeof R[j].cat); |
| 58 | for(int m=0;m<NSPEC;m++) if(urand()<PCAT){ R[j].cat[m]=1; catalyses[m]=1; } |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | /* a container is a count vector. one step: pick two molecules, fire a matching catalysed reaction, |
| 63 | produce the product, and evict a random molecule to hold capacity. */ |
| 64 | static void react(int *n,int cap,int steps){ |
| 65 | for(int t=0;t<steps;t++){ |
| 66 | int tot=0; for(int m=0;m<NSPEC;m++) tot+=n[m]; |
| 67 | if(tot<2) return; |
| 68 | int pa=rint_(tot), pb=rint_(tot), a=-1,b=-1,acc=0; |
| 69 | for(int m=0;m<NSPEC;m++){ acc+=n[m]; if(a<0&&pa<acc) a=m; if(b<0&&pb<acc) b=m; } |
| 70 | if(a<0||b<0) continue; |
| 71 | int j0=rint_(NRXN); |
| 72 | for(int d=0;d<NRXN;d++){ |
| 73 | int j=(j0+d)%NRXN; |
| 74 | if(!((R[j].a==a&&R[j].b==b)||(R[j].a==b&&R[j].b==a))) continue; |
| 75 | int cz=0; for(int m=0;m<NSPEC;m++) if(R[j].cat[m]&&n[m]>0){cz=1;break;} |
| 76 | if(!cz) break; |
| 77 | int ev=rint_(tot),e=-1; acc=0; |
| 78 | for(int m=0;m<NSPEC;m++){ acc+=n[m]; if(ev<acc){ e=m; break; } } |
| 79 | if(e>=0 && n[e]>0 && e>=NFOOD) n[e]--; /* food is buffered, not evicted */ |
| 80 | n[R[j].prod]++; |
| 81 | (void)cap; |
| 82 | break; |
| 83 | } |
| 84 | } |
| 85 | } |
| 86 | static double catfrac(const int *n){ |
| 87 | int tot=0,c=0; |
| 88 | for(int m=0;m<NSPEC;m++){ tot+=n[m]; if(catalyses[m]) c+=n[m]; } |
| 89 | return tot? (double)c/tot : 0.0; |
| 90 | } |
| 91 | |
| 92 | static int pool[512][NSPEC]; |
| 93 | |
| 94 | /* mode 0 = WELL-MIXED (one container of TOTAL), 1 = COMPARTMENTS + selection, 2 = COMPARTMENTS, no |
| 95 | selection (reseed from a RANDOM parent). Molecule count identical in every arm. */ |
| 96 | static double run(int mode,int csz){ |
| 97 | int ncomp = (mode==0)? 1 : TOTAL/csz; |
| 98 | int cap = (mode==0)? TOTAL : csz; |
| 99 | if(ncomp>512) ncomp=512; |
| 100 | for(int c=0;c<ncomp;c++){ |
| 101 | memset(pool[c],0,sizeof pool[c]); |
| 102 | for(int i=0;i<cap;i++) pool[c][rint_(NSPEC)]++; |
| 103 | } |
| 104 | int rounds = (mode==0)? STEPS*8 : STEPS/4; |
| 105 | for(int g=0;g<20;g++){ |
| 106 | for(int c=0;c<ncomp;c++) react(pool[c],cap,rounds); |
| 107 | if(mode==0) continue; |
| 108 | static int nxt[512][NSPEC]; |
| 109 | double tot=0,cum[512]; |
| 110 | for(int c=0;c<ncomp;c++){ tot += 0.01 + catfrac(pool[c]); cum[c]=tot; } |
| 111 | for(int c=0;c<ncomp;c++){ |
| 112 | int par; |
| 113 | if(mode==1){ double r=urand()*tot; int lo=0,hi=ncomp-1; |
| 114 | while(lo<hi){ int mid=(lo+hi)/2; if(cum[mid]<r) lo=mid+1; else hi=mid; } par=lo; } |
| 115 | else par=rint_(ncomp); /* M3 control: selection removed */ |
| 116 | memset(nxt[c],0,sizeof nxt[c]); |
| 117 | int tt=0; for(int m=0;m<NSPEC;m++) tt+=pool[par][m]; |
| 118 | for(int i=0;i<cap;i++){ /* random assortment from the parent */ |
| 119 | if(tt<1) break; |
| 120 | int p=rint_(tt),acc=0; |
| 121 | for(int m=0;m<NSPEC;m++){ acc+=pool[par][m]; if(p<acc){ nxt[c][m]++; break; } } |
| 122 | } |
| 123 | } |
| 124 | for(int c=0;c<ncomp;c++) memcpy(pool[c],nxt[c],sizeof nxt[c]); |
| 125 | } |
| 126 | double s=0; for(int c=0;c<ncomp;c++) s+=catfrac(pool[c]); |
| 127 | return s/ncomp; |
| 128 | } |
| 129 | |
| 130 | int main(void) |
| 131 | { |
| 132 | unsigned long long seeds[5]={0x9e3779b97f4a7c15ULL,0xd1b54a32d192ed03ULL,0x2545f4914f6cdd1dULL, |
| 133 | 0x123456789abcdefULL,0xfeedface12345678ULL}; |
| 134 | int NS=5; |
| 135 | printf("=== MILESTONE 3: counts. Can the substrate now express what rung 4 is FOR? ===\n"); |
| 136 | printf(" container.c: RAF is monotone, so a presence/absence type cannot express\n"); |
| 137 | printf(" compartmentalisation — 0 of 1200 containers held closure the mixture lacked. The type\n"); |
| 138 | printf(" now carries ABUNDANCES; parasites (species that catalyse nothing) fall out of the\n"); |
| 139 | printf(" random network rather than being declared. %d molecules in EVERY arm.\n\n",TOTAL); |
| 140 | printf(" catalyst fraction (parasites are the rest); ribozyme.c: well-mixed LOSES to size-8\n\n"); |
| 141 | printf(" comp size | WELL-MIXED | COMPARTMENTS+sel | COMPARTMENTS, no sel\n"); |
| 142 | printf(" ----------+------------+------------------+---------------------\n"); |
| 143 | int sizes[3]={8,16,48}, wins=0, ctrl_ok=1; double wm=0; |
| 144 | for(int z=0;z<3;z++){ |
| 145 | double a=0,b=0,c=0; |
| 146 | for(int s=0;s<NS;s++){ |
| 147 | RNG=seeds[s]; net_random(); a+=run(0,sizes[z]); |
| 148 | RNG=seeds[s]; net_random(); b+=run(1,sizes[z]); |
| 149 | RNG=seeds[s]; net_random(); c+=run(2,sizes[z]); |
| 150 | } |
| 151 | a/=NS;b/=NS;c/=NS; wm=a; |
| 152 | printf(" %2d | %.3f | %.3f | %.3f\n",sizes[z],a,b,c); |
| 153 | if(b>a+0.02) wins++; |
| 154 | if(c>b-0.01) ctrl_ok=0; /* selection must beat no-selection, or it is not selection */ |
| 155 | } |
| 156 | printf("\n M1 dynamics sustain something : %s\n", (wm>0.02&&wm<0.98)?"YES":"NO — degenerate, nothing to compare"); |
| 157 | printf(" M2 compartments beat well-mixed : %d/3 sizes\n",wins); |
| 158 | printf(" M3 selection beats no-selection : %s\n\n", ctrl_ok?"YES":"NO — compartmentalisation alone"); |
| 159 | if(!(wm>0.02&&wm<0.98)) |
| 160 | printf(" => DEGENERATE. The dynamics collapse or saturate, so nothing is being compared.\n" |
| 161 | " Counts are in the type but the reactor is wrong. Invalid, not a null.\n"); |
| 162 | else if(wins>=2 && ctrl_ok) |
| 163 | printf(" => THE TYPE NOW EXPRESSES RUNG 4. Compartments beat well-mixed, and selection beats\n" |
| 164 | " compartmentalisation alone — the ordering ribozyme.c found, reproduced in a\n" |
| 165 | " general substrate that also expresses rungs 1 and 2. Counts were the missing piece.\n"); |
| 166 | else |
| 167 | printf(" => HONEST NULL. Counts alone do not reproduce ribozyme.c's ordering here, so the\n" |
| 168 | " missing piece is not merely abundance. The substrate expresses rungs 1 and 2 and\n" |
| 169 | " still cannot express what compartments are for.\n"); |
| 170 | return 0; |
| 171 | } |