| … | … | @@ -0,0 +1,246 @@ |
| 1 | /* fidfloor.c — INVALID AS WRITTEN. DO NOT READ ITS NUMBERS AS A RESULT. |
| 2 | * |
| 3 | * ⚠️ F1 FAILED: the MIXED baseline returns 0.000 at EVERY fidelity, including f=0.98 where |
| 4 | * polymer.c reports emergence at 0.92. A baseline that does not reproduce the thing it is a |
| 5 | * baseline for makes every other column uninterpretable. Two faults, one identified, one not: |
| 6 | * |
| 7 | * (1) IDENTIFIED — MIXED is not well-mixed. It is 120 isolated compartments of 24 with division |
| 8 | * disabled, i.e. MORE compartmentalized than the arms it is supposed to be the control for, |
| 9 | * not less. polymer.c is ONE population of 800 with global fitness-weighted replication. |
| 10 | * A fix would be to keep the machinery and shuffle strands across compartments each step. |
| 11 | * (2) NOT IDENTIFIED — populations collapse (mean 168 against ~2880 initial in MIXED). The |
| 12 | * material budget arithmetic does not explain this; something else is wrong. |
| 13 | * |
| 14 | * What the run appeared to show, and why it means nothing: SELECT rose with f (0.353 at 0.95, |
| 15 | * 0.628 at 0.98) while MIXED and BLIND sat at 0.000. But 0.000 is EXTINCTION, so that comparison |
| 16 | * says only "the function-coupled arm survived and the others died" — a survival effect, not a |
| 17 | * fidelity-floor effect. It is confounded on top of that: SELECT ran 8099 replications against |
| 18 | * BLIND's 3308, exactly the population/replication confound the header promised to watch for. |
| 19 | * |
| 20 | * Kept in the tree as a record of an invalid experiment, NOT written into SYNTHESIS. Anyone |
| 21 | * resuming should fix (1), diagnose (2), and re-establish F1 before reading any other column. |
| 22 | * |
| 23 | * ---- original header follows ---- |
| 24 | * |
| 25 | * fidfloor.c — does EMERGENT compartment selection lower the fidelity floor? (rung 1+4 -> 2) |
| 26 | * |
| 27 | * THE PARADOX BEING TESTED. polymer.c supplied no copier: the only reproduction physics is |
| 28 | * template-directed synthesis with per-base fidelity f. It found heritable FUNCTION emerges only |
| 29 | * above f ~ 0.98, while un-catalyzed templating supplies ~0.85 — and the replicase that would close |
| 30 | * the gap is itself a functional sequence needing that fidelity FIRST. Eigen's bootstrap paradox, |
| 31 | * reproduced from below. |
| 32 | * |
| 33 | * THE REASON IT MIGHT NOT BE A WALL. That threshold was measured in a SINGLE-LEVEL population. Error |
| 34 | * thresholds are known to relax under multilevel selection — that is the stochastic corrector's whole |
| 35 | * point. And chemoton.c now gives compartment-level selection that EMERGES (growth coupled to the |
| 36 | * chemistry's own output, no fitness-proportional sampling anywhere) rather than being decreed. So: |
| 37 | * run polymer.c's dynamics inside chemoton.c's compartments and ask whether the floor drops from |
| 38 | * 0.98 toward 0.85. If it does, the paradox dissolves in the other order — you do not get accurate |
| 39 | * copying and then compartments, you get compartments and THEN accurate copying becomes affordable. |
| 40 | * |
| 41 | * WHAT IS COPIED FROM WHERE, VERBATIM: |
| 42 | * from polymer.c — binary strands, canonical form min(s,~s) so a strand and its complement are one |
| 43 | * functional unit, fitness exp(SS*matches) against a random target motif, and replicate() as |
| 44 | * template-directed synthesis complementing each bit with probability f. Nothing rewards a |
| 45 | * particular replicator identity, only the world-supplied function. |
| 46 | * from chemoton.c — conserved rationed material, uptake driving growth, division at a size |
| 47 | * threshold with random assortment, per-strand death returning material. No group-selection step. |
| 48 | * |
| 49 | * ARMS (all three share the SAME compartment machinery; only uptake differs): |
| 50 | * 1 SELECT uptake proportional to the compartment's mean functional match — the emergent |
| 51 | * between-compartment selection. |
| 52 | * 2 BLIND uptake proportional to compartment SIZE, blind to function. Compartments exist, divide |
| 53 | * and die identically; only the selection is removed. THIS IS THE CONTROL — it separates |
| 54 | * compartment SELECTION from mere compartmentalization. |
| 55 | * 3 MIXED one compartment holding everything: the polymer.c single-level baseline. |
| 56 | * |
| 57 | * PRE-REGISTERED: |
| 58 | * F1 MIXED reproduces polymer.c — function emerges only at f ≈ 0.98, not at 0.85–0.90. |
| 59 | * F2 SELECT lowers the floor — function emerges at a fidelity where MIXED fails. |
| 60 | * F3 CONTROL: BLIND does NOT lower it. If it does, compartmentalization alone is responsible and |
| 61 | * the emergent selection is doing nothing. |
| 62 | * F4 TEST OF THE TEST: compartments must actually divide and turn over, or there was no |
| 63 | * compartment-level reproduction to select on. |
| 64 | * |
| 65 | * MATCHED-CONDITION DISCIPLINE (today's lesson: one knob must not move two variables). Turning |
| 66 | * uptake-selection on also changes population size and total replication count. Both are REPORTED |
| 67 | * per arm so the reader can see whether the arms are comparable; if they diverge sharply, any floor |
| 68 | * shift is confounded by sheer number of replication events and must be read as such. |
| 69 | * |
| 70 | * build: cc -O2 -Wall -std=c11 -o fidfloor fidfloor.c -lm |
| 71 | */ |
| 72 | #include <math.h> |
| 73 | #include <stdio.h> |
| 74 | #include <stdint.h> |
| 75 | |
| 76 | #define L 16 /* polymer.c's shorter case, where it found f* = 0.98 */ |
| 77 | #define SS 0.30 /* selection strength, verbatim from polymer.c */ |
| 78 | #define D 0.30 /* per-step degradation, verbatim */ |
| 79 | #define STEPS 140 /* verbatim */ |
| 80 | #define MAXCOMP 2000 |
| 81 | #define MAXSZ 24 |
| 82 | #define COSTUNIT 1.0 |
| 83 | #define TOTALMAT 3000.0 |
| 84 | #define UPTAKE 0.55 |
| 85 | |
| 86 | static unsigned long long RNG; |
| 87 | static unsigned long long xs(void){ RNG^=RNG<<13; RNG^=RNG>>7; RNG^=RNG<<17; return RNG; } |
| 88 | static double urand(void){ return (double)(xs()>>11)*(1.0/9007199254740992.0); } |
| 89 | |
| 90 | static uint64_t MASK, FTAR_C; |
| 91 | static uint64_t canon(uint64_t s){ uint64_t c=(~s)&MASK; return s<c?s:c; } |
| 92 | static int matchC(uint64_t s){ return L - __builtin_popcountll(canon(s) ^ FTAR_C); } |
| 93 | static uint64_t replicate(uint64_t t,double f){ |
| 94 | uint64_t c=0; |
| 95 | for(int i=0;i<L;i++){ int b=(int)((t>>i)&1); int nb=(urand()<f)?(1-b):b; if(nb) c|=(1ULL<<i); } |
| 96 | return c; |
| 97 | } |
| 98 | |
| 99 | typedef struct { uint64_t s[MAXSZ]; int n; double mat; } Comp; |
| 100 | static Comp C[MAXCOMP]; |
| 101 | static int ncomp; |
| 102 | static double pool; |
| 103 | static double demand[MAXCOMP]; |
| 104 | |
| 105 | /* mode 0 = SELECT (uptake ~ mean match), 1 = BLIND (uptake ~ size), 2 = MIXED (one big compartment) */ |
| 106 | static double run(int mode,double f,int divsz,double *start,double *ndiv,double *pop,double *nrep) |
| 107 | { |
| 108 | MASK=(1ULL<<L)-1; |
| 109 | uint64_t ft=0; for(int i=0;i<L;i++) if(urand()<0.5) ft|=(1ULL<<i); FTAR_C=canon(ft); |
| 110 | |
| 111 | int seed_n = (mode==2)? MAXSZ : (divsz/2>2?divsz/2:2); |
| 112 | ncomp = (mode==2)? 120 : 600; |
| 113 | for(int c=0;c<ncomp;c++){ |
| 114 | C[c].n=seed_n; C[c].mat=0; |
| 115 | for(int k=0;k<seed_n;k++){ uint64_t v=0; for(int i=0;i<L;i++) if(urand()<0.5) v|=(1ULL<<i); C[c].s[k]=v; } |
| 116 | } |
| 117 | { double m=0; long t=0; for(int c=0;c<ncomp;c++) for(int k=0;k<C[c].n;k++){ m+=matchC(C[c].s[k]); t++; } *start=m/((double)t*L); } |
| 118 | { long u=0; for(int c=0;c<ncomp;c++) u+=C[c].n; pool=TOTALMAT-u*COSTUNIT; } |
| 119 | |
| 120 | double totdiv=0, totrep=0, popsum=0; |
| 121 | |
| 122 | for(int g=0;g<STEPS;g++){ |
| 123 | /* degradation, verbatim from polymer.c; material returns to the pool */ |
| 124 | for(int c=0;c<ncomp;c++) |
| 125 | for(int k=0;k<C[c].n;){ |
| 126 | if(urand()<D){ C[c].s[k]=C[c].s[C[c].n-1]; C[c].n--; pool+=COSTUNIT; } |
| 127 | else k++; |
| 128 | } |
| 129 | |
| 130 | /* uptake, rationed when demand exceeds supply */ |
| 131 | double Dm=0; |
| 132 | for(int c=0;c<ncomp;c++){ |
| 133 | double out; |
| 134 | if(mode==0){ double m=0; for(int k=0;k<C[c].n;k++) m+=matchC(C[c].s[k]); out = C[c].n? (m/C[c].n)/L*C[c].n : 0; } |
| 135 | else out = 0.5*C[c].n; |
| 136 | demand[c]=UPTAKE*out; Dm+=demand[c]; |
| 137 | } |
| 138 | double fr=(Dm>0&&Dm>pool)?pool/Dm:1.0; |
| 139 | for(int c=0;c<ncomp;c++){ double t=demand[c]*fr; C[c].mat+=t; pool-=t; } |
| 140 | |
| 141 | /* growth: material buys template-directed replication, parent by fitness WITHIN compartment */ |
| 142 | for(int c=0;c<ncomp;c++){ |
| 143 | int cap = (mode==2)? MAXSZ : MAXSZ; |
| 144 | while(C[c].mat>=COSTUNIT && C[c].n<cap && C[c].n>0){ |
| 145 | double tot=0,cum[MAXSZ]; |
| 146 | for(int k=0;k<C[c].n;k++){ tot+=exp(SS*matchC(C[c].s[k])); cum[k]=tot; } |
| 147 | double r=urand()*tot; int lo=0,hi=C[c].n-1; |
| 148 | while(lo<hi){ int mid=(lo+hi)/2; if(cum[mid]<r) lo=mid+1; else hi=mid; } |
| 149 | C[c].s[C[c].n]=replicate(C[c].s[lo],f); C[c].n++; C[c].mat-=COSTUNIT; totrep++; |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | /* division + compartment death (skipped in MIXED, which is one big well-mixed pool) */ |
| 154 | if(mode!=2){ |
| 155 | int base=ncomp; |
| 156 | for(int c=0;c<base;c++){ |
| 157 | if(C[c].n>=divsz && ncomp<MAXCOMP){ |
| 158 | Comp *p=&C[c],*q=&C[ncomp]; |
| 159 | q->n=0; q->mat=p->mat*0.5; p->mat*=0.5; int w=0; |
| 160 | for(int k=0;k<p->n;k++){ if(urand()<0.5) q->s[q->n++]=p->s[k]; else p->s[w++]=p->s[k]; } |
| 161 | p->n=w; ncomp++; totdiv++; |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 | for(int c=0;c<ncomp;){ if(C[c].n<=0){ pool+=C[c].mat; C[c]=C[ncomp-1]; ncomp--; } else c++; } |
| 166 | if(ncomp<=0) break; |
| 167 | { long u=0; for(int c=0;c<ncomp;c++) u+=C[c].n; popsum+=u; } |
| 168 | } |
| 169 | |
| 170 | double m=0; long t=0; |
| 171 | for(int c=0;c<ncomp;c++) for(int k=0;k<C[c].n;k++){ m+=matchC(C[c].s[k]); t++; } |
| 172 | *ndiv=totdiv/STEPS; *pop=popsum/STEPS; *nrep=totrep; |
| 173 | return t? m/((double)t*L) : 0.0; |
| 174 | } |
| 175 | |
| 176 | int main(void) |
| 177 | { |
| 178 | unsigned long long seeds[6]; |
| 179 | seeds[0]=0x9e3779b97f4a7c15ULL; seeds[1]=0xd1b54a32d192ed03ULL; seeds[2]=0x2545f4914f6cdd1dULL; |
| 180 | for(int i=3;i<6;i++) seeds[i]=0x9e3779b97f4a7c15ULL ^ (0x100000001b3ULL*(unsigned long long)(i+1)); |
| 181 | int NS=6; |
| 182 | |
| 183 | printf("=== FIDELITY FLOOR: does emergent compartment selection lower Eigen's bootstrap wall? ===\n"); |
| 184 | printf(" polymer.c (single level) found heritable function needs f >= 0.98, while un-catalyzed\n"); |
| 185 | printf(" templating supplies ~0.85 — and the replicase closing that gap needs the fidelity first.\n"); |
| 186 | printf(" Here polymer.c's dynamics run INSIDE chemoton.c's emergent compartments. L=%d, %d seeds.\n",L,NS); |
| 187 | printf(" SELECT = uptake ~ mean functional match. BLIND = uptake ~ size (compartments, no\n"); |
| 188 | printf(" selection) — THE CONTROL. MIXED = one big pool, the single-level baseline.\n\n"); |
| 189 | |
| 190 | double fs[]={0.80,0.85,0.90,0.95,0.98}; |
| 191 | int nF=5, divsz=8; |
| 192 | double sel[8],bli[8],mix[8], st=0; |
| 193 | double dv=0, pS=0, pB=0, pM=0, rS=0, rB=0, rM=0; |
| 194 | |
| 195 | printf(" f | MIXED (1 level) | BLIND (comps, no sel) | SELECT (emergent sel)\n"); |
| 196 | printf(" -------+-----------------+-----------------------+----------------------\n"); |
| 197 | for(int i=0;i<nF;i++){ |
| 198 | double a=0,b=0,c=0,s0=0,d1=0,p1=0,p2=0,p3=0,r1=0,r2=0,r3=0; |
| 199 | for(int s=0;s<NS;s++){ |
| 200 | double s1,dd,pp,rr; |
| 201 | RNG=seeds[s]; c+=run(2,fs[i],divsz,&s1,&dd,&pp,&rr); p3+=pp; r3+=rr; |
| 202 | RNG=seeds[s]; b+=run(1,fs[i],divsz,&s1,&dd,&pp,&rr); p2+=pp; r2+=rr; |
| 203 | RNG=seeds[s]; a+=run(0,fs[i],divsz,&s1,&dd,&pp,&rr); s0+=s1; d1+=dd; p1+=pp; r1+=rr; |
| 204 | } |
| 205 | sel[i]=a/NS; bli[i]=b/NS; mix[i]=c/NS; st=s0/NS; |
| 206 | dv=d1/NS; pS=p1/NS; pB=p2/NS; pM=p3/NS; rS=r1/NS; rB=r2/NS; rM=r3/NS; |
| 207 | printf(" %.2f | %.3f | %.3f | %.3f\n", |
| 208 | fs[i],mix[i],bli[i],sel[i]); |
| 209 | } |
| 210 | printf("\n start (random soup) %.3f\n",st); |
| 211 | printf(" diagnostics at f=0.98: divisions/step %.1f | mean pop MIXED %.0f BLIND %.0f SELECT %.0f\n",dv,pM,pB,pS); |
| 212 | printf(" total replications MIXED %.0f BLIND %.0f SELECT %.0f\n",rM,rB,rS); |
| 213 | |
| 214 | /* emergence criterion, verbatim from polymer.c: end > 0.85 AND end > start + 0.2 */ |
| 215 | int fM=-1,fB=-1,fS=-1; |
| 216 | for(int i=0;i<nF;i++){ |
| 217 | if(fM<0 && mix[i]>0.85 && mix[i]>st+0.2) fM=i; |
| 218 | if(fB<0 && bli[i]>0.85 && bli[i]>st+0.2) fB=i; |
| 219 | if(fS<0 && sel[i]>0.85 && sel[i]>st+0.2) fS=i; |
| 220 | } |
| 221 | printf("\n floor (first f where function emerges, polymer.c's criterion):\n"); |
| 222 | printf(" MIXED %s%.2f\n", fM<0?">":"", fM<0?fs[nF-1]:fs[fM]); |
| 223 | printf(" BLIND %s%.2f\n", fB<0?">":"", fB<0?fs[nF-1]:fs[fB]); |
| 224 | printf(" SELECT %s%.2f\n\n", fS<0?">":"", fS<0?fs[nF-1]:fs[fS]); |
| 225 | |
| 226 | int turned = dv>0.5; |
| 227 | if(!turned) |
| 228 | printf(" => F4 FAILED, THE TEST DID NOT RUN. Compartments did not divide, so there was no\n" |
| 229 | " compartment-level reproduction to select on. Invalid experiment, not a null.\n"); |
| 230 | else if(fS>=0 && (fM<0 || fs[fS]<fs[fM]) && (fB<0 || fs[fS]<fs[fB])) |
| 231 | printf(" => THE FLOOR DROPS, AND ONLY WITH SELECTION. Emergent compartment selection lets\n" |
| 232 | " heritable function appear at a fidelity where both the single-level baseline and\n" |
| 233 | " compartments-without-selection fail. Eigen's bootstrap paradox is not a wall in the\n" |
| 234 | " order it is usually posed: you do not need accurate copying before compartments —\n" |
| 235 | " compartments make accurate copying affordable. CHECK the population and replication\n" |
| 236 | " diagnostics before claiming it: if SELECT ran far more replications, the shift may\n" |
| 237 | " be sheer number of trials rather than multilevel selection.\n"); |
| 238 | else if(fS>=0 && fB>=0 && fs[fS]>=fs[fB]) |
| 239 | printf(" => COMPARTMENTS, NOT SELECTION. The floor drops (or does not) identically with and\n" |
| 240 | " without between-compartment selection, so mere compartmentalization explains it.\n"); |
| 241 | else |
| 242 | printf(" => HONEST NULL. Emergent compartment selection does NOT lower the fidelity floor here;\n" |
| 243 | " the bootstrap paradox survives multilevel selection in this model. That is a real\n" |
| 244 | " result: it says the wall is fidelity itself, not the level selection acts on.\n"); |
| 245 | return 0; |
| 246 | } |