| … | … | @@ -47,3 +47,4 @@ chemoton |
| 47 | 47 | rafcomp |
| 48 | 48 | fidfloor |
| 49 | 49 | genome |
| 50 | replicase |
| … | … | @@ -61,6 +61,7 @@ |
| 61 | 61 | #define NCOMP 600 |
| 62 | 62 | #define CSZ 8 /* small compartments: the regime the corrector needs */ |
| 63 | 63 | #define GEN 200 |
| 64 | #define PREAD 0.50 /* chance a picked template is READ rather than copied true */ | |
| 64 | 65 | #define BETA 2.0 /* non-folders replicate faster — the parasite advantage stays */ |
| 65 | 66 | |
| 66 | 67 | static unsigned long long RNG; |
| … | … | @@ -73,19 +74,31 @@ static int isfold(uint32_t s){ return phi(s)>=THETA; } |
| 73 | 74 | static int typeof_(uint32_t s){ return (int)((s>>NFOLD)&7); } |
| 74 | 75 | static uint32_t mut(uint32_t s,double mu){ for(int i=0;i<NFOLD+3;i++) if(urand()<mu) s^=(1u<<i); return s; } |
| 75 | 76 | |
| 77 | static int TARGET[NTYPE]; /* world-supplied: which types are worth having (payoff mode 1) */ | |
| 78 | static int NTARG=4; | |
| 76 | 79 | static uint32_t cell[NCOMP][CSZ], ncell[NCOMP][CSZ]; |
| 77 | 80 | static double cum[NCOMP]; |
| 78 | 81 | |
| 79 | /* distinct types present among CATALYSTS — the compartment's functional repertoire */ | |
| 80 | static int repertoire(const uint32_t *c){ | |
| 82 | /* payoff 0 = DIVERSITY: any distinct catalyst types count (genome.c's original). | |
| 83 | payoff 1 = TARGET: only types in a world-supplied target set count. | |
| 84 | The difference is the whole point: under DIVERSITY any repertoire is as good as any other, so | |
| 85 | there is nothing worth remembering and a dice-roll beats a memory. Under TARGET a SPECIFIC | |
| 86 | configuration beats a random one, so faithful reading has something to be faithful TO. */ | |
| 87 | static int repertoire_mode(const uint32_t *c,int payoff){ | |
| 81 | 88 | int seen[NTYPE]; for(int t=0;t<NTYPE;t++) seen[t]=0; |
| 82 | int d=0; for(int k=0;k<CSZ;k++) if(isfold(c[k])){ int t=typeof_(c[k]); if(!seen[t]){seen[t]=1;d++;} } | |
| 89 | int d=0; | |
| 90 | for(int k=0;k<CSZ;k++) if(isfold(c[k])){ int t=typeof_(c[k]); if(!seen[t]){ seen[t]=1; if(payoff==0||TARGET[t]) d++; } } | |
| 83 | 91 | return d; |
| 84 | 92 | } |
| 93 | static int repertoire(const uint32_t *c){ return repertoire_mode(c,0); } | |
| 85 | 94 | |
| 86 | 95 | /* mode 0 = READ OFF, 1 = READ ON, 2 = SCRAMBLED control (reads, but type bits randomised) */ |
| 87 | static double run(int mode,double mu,double *tmpl_frac,double *cat_frac,double *rep_out) | |
| 96 | static double run(int mode,double mu,int payoff,double *tmpl_frac,double *cat_frac,double *rep_out) | |
| 88 | 97 | { |
| 98 | /* world-supplied target: a random NTARG-subset of the types, redrawn per run so no particular | |
| 99 | set is special-cased. Same shape as polymer.c's random target motif. */ | |
| 100 | for(int t=0;t<NTYPE;t++) TARGET[t]=0; | |
| 101 | for(int got=0;got<NTARG;){ int t=(int)(xs()%NTYPE); if(!TARGET[t]){ TARGET[t]=1; got++; } } | |
| 89 | 102 | for(int c=0;c<NCOMP;c++) |
| 90 | 103 | for(int k=0;k<CSZ;k++){ uint32_t v=0; for(int i=0;i<NFOLD+3;i++) if(urand()<0.5) v|=(1u<<i); cell[c][k]=v; } |
| 91 | 104 | |
| … | … | @@ -100,12 +113,17 @@ static double run(int mode,double mu,double *tmpl_frac,double *cat_frac,double * |
| 100 | 113 | double r=urand()*tot; int lo=0,hi=CSZ-1; |
| 101 | 114 | while(lo<hi){ int mid=(lo+hi)/2; if(cw[mid]<r) lo=mid+1; else hi=mid; } |
| 102 | 115 | uint32_t par=cell[c][lo], child; |
| 103 | if(mode!=0 && !isfold(par) && mach>=0){ | |
| 104 | /* READ: a catalyst's fold bits + this template's type bits -> catalyst of that type. | |
| 105 | Supplied physics. Nothing here rewards holding templates. */ | |
| 116 | if(mode!=0 && !isfold(par) && mach>=0 && urand()<PREAD){ | |
| 117 | /* READ (template -> catalyst): a catalyst's fold bits + this template's type bits. | |
| 118 | v1/v2 made this the ONLY thing a template could do, so reading was a dead end for | |
| 119 | the template lineage — it could be useful or persist, never both. Real genomes are | |
| 120 | copied AS genomes and SEPARATELY read. Now reading happens with probability PREAD | |
| 121 | and otherwise the template is copied as a template, exactly as a catalyst is | |
| 122 | copied as a catalyst. Both classes propagate only by copying; reading is a | |
| 123 | FUNCTION templates provide, not an extra route to propagate. */ | |
| 106 | 124 | uint32_t ty = (mode==2) ? (uint32_t)(xs()&7) : (uint32_t)typeof_(par); |
| 107 | 125 | child = (cell[c][mach] & ((1u<<NFOLD)-1)) | (ty<<NFOLD); |
| 108 | } else child = par; /* plain copy: catalyst or unread template */ | |
| 126 | } else child = par; /* copy true: catalyst->catalyst, template->template */ | |
| 109 | 127 | ncell[c][k]=mut(child,mu); |
| 110 | 128 | } |
| 111 | 129 | } |
| … | … | @@ -113,7 +131,7 @@ static double run(int mode,double mu,double *tmpl_frac,double *cat_frac,double * |
| 113 | 131 | |
| 114 | 132 | /* ---- between compartment: DECREED selection on functional repertoire (flagged, not derived) ---- */ |
| 115 | 133 | double tot=0; |
| 116 | for(int c=0;c<NCOMP;c++){ tot += 0.05 + (double)repertoire(cell[c]); cum[c]=tot; } | |
| 134 | for(int c=0;c<NCOMP;c++){ tot += 0.05 + (double)repertoire_mode(cell[c],payoff); cum[c]=tot; } | |
| 117 | 135 | for(int c=0;c<NCOMP;c++){ |
| 118 | 136 | double r=urand()*tot; int lo=0,hi=NCOMP-1; |
| 119 | 137 | while(lo<hi){ int mid=(lo+hi)/2; if(cum[mid]<r) lo=mid+1; else hi=mid; } |
| … | … | @@ -124,7 +142,7 @@ static double run(int mode,double mu,double *tmpl_frac,double *cat_frac,double * |
| 124 | 142 | |
| 125 | 143 | long nt=0,nc=0; double rp=0; |
| 126 | 144 | for(int c=0;c<NCOMP;c++){ |
| 127 | rp += repertoire(cell[c]); | |
| 145 | rp += repertoire_mode(cell[c],payoff); | |
| 128 | 146 | for(int k=0;k<CSZ;k++){ if(isfold(cell[c][k])) nc++; else nt++; } |
| 129 | 147 | } |
| 130 | 148 | *tmpl_frac=(double)nt/((double)NCOMP*CSZ); |
| … | … | @@ -140,72 +158,58 @@ int main(void) |
| 140 | 158 | for(int i=3;i<6;i++) seeds[i]=0x9e3779b97f4a7c15ULL ^ (0x100000001b3ULL*(unsigned long long)(i+1)); |
| 141 | 159 | int NS=6; |
| 142 | 160 | |
| 143 | printf("=== PARASITE AS PROTO-GENOME: does information move onto the cheap molecule? ===\n"); | |
| 144 | printf(" A strand is %d FOLD bits + 3 TYPE bits. Folds (phi>=%d) => a CATALYST of its type.\n",NFOLD,THETA); | |
| 145 | printf(" Does not fold => a TEMPLATE carrying only type. A template protects 3 bits, a catalyst\n"); | |
| 146 | printf(" protects %d — the same information, fewer constrained bits, IF it can be read.\n",NFOLD+3); | |
| 147 | printf(" SUPPLIED: reading (a catalyst's fold + a template's type -> a catalyst of that type).\n"); | |
| 148 | printf(" NOT SUPPLIED: any reward for holding templates. They fold nothing, cost the same, and\n"); | |
| 149 | printf(" keep the parasite advantage (non-folders replicate %.0fx faster). %d comps x %d, %d gens.\n\n",1+BETA,NCOMP,CSZ,GEN); | |
| 161 | printf("=== PARASITE AS PROTO-GENOME v2: does the PAYOFF decide whether memory pays? ===\n"); | |
| 162 | printf(" v1 found reading raises function (+0.92 at high mu) but SCRAMBLED reading — random type\n"); | |
| 163 | printf(" instead of the template's — BEAT faithful reading everywhere. Diagnosis: the payoff\n"); | |
| 164 | printf(" rewarded DIVERSITY, and randomness supplies diversity better than fidelity does. A world\n"); | |
| 165 | printf(" where any repertoire is as good as any other has nothing worth remembering.\n"); | |
| 166 | printf(" v2 adds a TARGET payoff: only %d of %d types count, drawn at random per run. Now a\n",4,NTYPE); | |
| 167 | printf(" SPECIFIC configuration beats a random one. PREDICTION: the control INVERTS.\n\n"); | |
| 150 | 168 | |
| 151 | printf(" mu | READ OFF: rep / tmpl | READ ON: rep / tmpl | SCRAMBLED: rep / tmpl\n"); | |
| 152 | printf(" ------+----------------------+---------------------+----------------------\n"); | |
| 153 | 169 | double mus[]={0.005,0.01,0.02,0.04,0.08}; |
| 154 | int nM=5, coexist=0, on_wins=0, scr_flat=1; | |
| 155 | double gap_lo=0, gap_hi=0; | |
| 170 | int nM=5; | |
| 171 | const char *pn[2]={"DIVERSITY (v1)","TARGET (v2)"}; | |
| 172 | int inverted=0, div_scr_wins=0, tgt_faith_wins=0; | |
| 156 | 173 | |
| 157 | for(int i=0;i<nM;i++){ | |
| 158 | double r0=0,t0=0,c0=0, r1=0,t1=0,c1=0, r2=0,t2=0,c2=0; | |
| 159 | for(int s=0;s<NS;s++){ | |
| 160 | double tf,cf,rp; | |
| 161 | RNG=seeds[s]; run(0,mus[i],&tf,&cf,&rp); r0+=rp; t0+=tf; c0+=cf; | |
| 162 | RNG=seeds[s]; run(1,mus[i],&tf,&cf,&rp); r1+=rp; t1+=tf; c1+=cf; | |
| 163 | RNG=seeds[s]; run(2,mus[i],&tf,&cf,&rp); r2+=rp; t2+=tf; c2+=cf; | |
| 174 | for(int pay=0;pay<2;pay++){ | |
| 175 | printf(" --- payoff = %s ---\n",pn[pay]); | |
| 176 | printf(" mu | READ OFF | READ ON (faithful) | SCRAMBLED | faithful - scrambled\n"); | |
| 177 | printf(" ------+----------+--------------------+-----------+---------------------\n"); | |
| 178 | for(int i=0;i<nM;i++){ | |
| 179 | double r0=0,r1=0,r2=0; | |
| 180 | for(int s=0;s<NS;s++){ | |
| 181 | double tf,cf,rp; | |
| 182 | RNG=seeds[s]; run(0,mus[i],pay,&tf,&cf,&rp); r0+=rp; | |
| 183 | RNG=seeds[s]; run(1,mus[i],pay,&tf,&cf,&rp); r1+=rp; | |
| 184 | RNG=seeds[s]; run(2,mus[i],pay,&tf,&cf,&rp); r2+=rp; | |
| 185 | } | |
| 186 | r0/=NS;r1/=NS;r2/=NS; | |
| 187 | double d=r1-r2; | |
| 188 | printf(" %.3f | %.2f | %.2f | %.2f | %+.3f\n",mus[i],r0,r1,r2,d); | |
| 189 | if(pay==0 && d<-0.02) div_scr_wins++; | |
| 190 | if(pay==1 && d>0.02) tgt_faith_wins++; | |
| 164 | 191 | } |
| 165 | r0/=NS;t0/=NS;c0/=NS; r1/=NS;t1/=NS;c1/=NS; r2/=NS;t2/=NS;c2/=NS; | |
| 166 | printf(" %.3f | %.2f / %.3f | %.2f / %.3f | %.2f / %.3f\n", | |
| 167 | mus[i], r0,t0, r1,t1, r2,t2); | |
| 168 | /* CORRECTED CRITERIA. The first version required TEMPLATE FRACTION TO RISE in both G2 and | |
| 169 | G3, which is backwards: reading CONSUMES templates by converting them into catalysts, so a | |
| 170 | working genome shows FEWER standing templates, not more. Judge on repertoire, and judge the | |
| 171 | control by whether faithful reading beats scrambled reading. */ | |
| 172 | if(c1>0.02 && t1>0.02) coexist=1; | |
| 173 | if(r1>r0+0.1) on_wins++; | |
| 174 | if(r2>r1+0.05) scr_flat=0; /* scrambled beating faithful = the information does nothing */ | |
| 175 | if(i==0) gap_lo=r1-r0; | |
| 176 | gap_hi=r1-r0; | |
| 192 | printf("\n"); | |
| 177 | 193 | } |
| 178 | printf("\n rep = mean distinct catalyst types per compartment (max %d); tmpl = template fraction\n\n",NTYPE); | |
| 194 | inverted = (div_scr_wins>=4 && tgt_faith_wins>=4); | |
| 179 | 195 | |
| 180 | printf(" G4 catalysts and templates coexist : %s\n", coexist?"YES":"NO — nothing to observe"); | |
| 181 | printf(" G2 READ ON beats READ OFF : %d/%d mutation rates\n", on_wins,nM); | |
| 182 | printf(" G3 faithful reading beats SCRAMBLED : %s\n", scr_flat?"YES":"NO — the INFORMATION does nothing"); | |
| 183 | printf(" G5 advantage grows with mu : %+.2f at mu=%.3f -> %+.2f at mu=%.3f\n\n", | |
| 184 | gap_lo,mus[0],gap_hi,mus[nM-1]); | |
| 196 | printf(" under DIVERSITY, scrambled beats faithful : %d/%d mutation rates\n",div_scr_wins,nM); | |
| 197 | printf(" under TARGET, faithful beats scrambled : %d/%d mutation rates\n\n",tgt_faith_wins,nM); | |
| 185 | 198 | |
| 186 | if(!coexist) | |
| 187 | printf(" => THE TEST DID NOT RUN. One class went extinct everywhere, so there was no offloading\n" | |
| 188 | " to observe. Invalid experiment, not a null.\n"); | |
| 189 | else if(!scr_flat) | |
| 190 | printf(" => THE INFORMATION DOES NOTHING. Reading raises functional repertoire (+0.14 at low mu\n" | |
| 191 | " to +0.92 at high mu, exactly as G5 predicted), but SCRAMBLED reading -- which\n" | |
| 192 | " produces a catalyst of a RANDOM type -- beats faithful reading at every mutation\n" | |
| 193 | " rate. So the gain is the mechanism CONVERTING non-folders into folders, not the\n" | |
| 194 | " information a template carries. Claim nothing about genomes.\n" | |
| 195 | " DIAGNOSIS: the payoff here rewards DIVERSITY of catalyst types, and randomness\n" | |
| 196 | " supplies diversity better than fidelity does. A world where any repertoire is as\n" | |
| 197 | " good as any other has nothing worth remembering, so memory cannot pay. The fix is a\n" | |
| 198 | " payoff on a SPECIFIC target set of types, where a particular configuration beats a\n" | |
| 199 | " random one -- then faithful reading has something to be faithful TO. NOT RUN.\n"); | |
| 200 | else if(on_wins>=nM-1) | |
| 201 | printf(" => THE SYSTEM OFFLOADED ITS INFORMATION. Given a way to read cheap non-folding strands,\n" | |
| 202 | " lineages keep them and carry more function than lineages that cannot — with the\n" | |
| 203 | " scrambled control flat, so it is the INFORMATION and not the mechanism. The molecule\n" | |
| 204 | " the whole arc treated as a parasite to be purged became the thing that remembers.\n"); | |
| 199 | if(inverted) | |
| 200 | printf(" => THE CONTROL INVERTS, AND THE PAYOFF IS WHAT DECIDES. The identical mechanism, the\n" | |
| 201 | " identical molecules, the identical parasite advantage — and whether carrying\n" | |
| 202 | " information on a cheap non-folding strand pays depends entirely on whether the world\n" | |
| 203 | " rewards a SPECIFIC configuration or merely variety. Memory is worthless in a world\n" | |
| 204 | " that will take any answer, and selected for in one that will not. That is a\n" | |
| 205 | " condition on the ENVIRONMENT for the genotype/phenotype split, not on the chemistry.\n"); | |
| 206 | else if(tgt_faith_wins>=4) | |
| 207 | printf(" => FAITHFUL WINS UNDER TARGET (%d/%d) but the diversity contrast did not come out as\n" | |
| 208 | " cleanly (%d/%d). The direction supports the diagnosis; report the asymmetry as-is.\n", | |
| 209 | tgt_faith_wins,nM,div_scr_wins,nM); | |
| 205 | 210 | else |
| 206 | printf(" => HONEST NULL. Reading is available and free, and the system does NOT offload:\n" | |
| 207 | " templates are purged as parasites anyway. Carrying information on a cheap molecule\n" | |
| 208 | " is not selected for here, so the genotype/phenotype split needs more than the\n" | |
| 209 | " opportunity — fidfloor.c says it is necessary, this says it is not sufficient.\n"); | |
| 211 | printf(" => HONEST NULL. Even with a target payoff, faithful reading does NOT beat scrambled.\n" | |
| 212 | " The diagnosis from v1 was wrong: giving the world something specific to want is not\n" | |
| 213 | " what makes memory pay, and the missing ingredient is still unidentified.\n"); | |
| 210 | 214 | return 0; |
| 211 | 215 | } |
| … | … | @@ -0,0 +1,212 @@ |
| 1 | /* replicase.c — the closed genotype/phenotype loop. Can it bootstrap? | |
| 2 | * | |
| 3 | * WHERE THIS COMES FROM. genome.c v1/v2 gave templates a fair test and they failed twice, but the | |
| 4 | * second failure exposed something I had been getting for free: TEMPLATE COPYING WAS FREE PHYSICS | |
| 5 | * (child = par, perfect, no cost). In reality copying a genome faithfully needs a REPLICASE, which | |
| 6 | * is itself a catalyst, which is itself specified by a template. That is the loop, and it is exactly | |
| 7 | * rung 1→2. | |
| 8 | * | |
| 9 | * Two flaws in genome.c are fixed here: | |
| 10 | * (1) a picked template ALWAYS became a catalyst when reading was on, so reading was a dead end for | |
| 11 | * the template lineage — it could be useful or persist, never both. Now a picked template is | |
| 12 | * READ with probability PREAD and otherwise COPIED TRUE, exactly as a catalyst is copied true. | |
| 13 | * Reading is a FUNCTION templates provide, not a route by which they propagate. | |
| 14 | * (2) template fidelity was free. Now it is CONDITIONAL on the compartment holding a catalyst of | |
| 15 | * the replicase type: with one, templates copy at MU*RLO; without, at MU*RHI. | |
| 16 | * | |
| 17 | * THE RAZOR. This is where encoding the answer is easiest, so, precisely: | |
| 18 | * SUPPLIED as physics: "a type-REPTYPE catalyst improves template fidelity in its compartment" — | |
| 19 | * a function, the same shape as ribozyme.c's supplied fold→catalysis map. | |
| 20 | * NOT SUPPLIED: any reward for being or holding a replicase. REPTYPE is EXCLUDED FROM THE TARGET | |
| 21 | * SET, so it contributes nothing to payoff directly — it is pure overhead unless the template | |
| 22 | * loop pays for itself. It is not seeded, not protected, not favoured in replication. Non-folders | |
| 23 | * still replicate faster, so the parasite advantage is intact and the replicase is a PUBLIC GOOD | |
| 24 | * within its compartment, free-ridable exactly as catalysis was in ribozyme.c. | |
| 25 | * MEASURED: whether lineages acquire and HOLD the replicase, and whether function is higher for it. | |
| 26 | * | |
| 27 | * THE CONTROL, built to today's matched-condition lesson. Turning the replicase effect on also | |
| 28 | * changes the average template mutation rate, which would confound any gain. So the control applies | |
| 29 | * the IDENTICAL two-rate structure conditioned on a COIN FLIP per compartment instead of on | |
| 30 | * replicase presence — same mutation-rate distribution, no type to select for. The realized | |
| 31 | * low-fidelity fraction is reported for both arms so the match is visible rather than assumed. | |
| 32 | * | |
| 33 | * PRE-REGISTERED: | |
| 34 | * R1 replicase frequency RISES above the control's. Lineages hold a type that pays them nothing | |
| 35 | * directly, because it protects the templates that carry everything else. | |
| 36 | * R2 target repertoire is HIGHER with the replicase effect than with the coin-flip control. | |
| 37 | * R3 TEST OF THE TEST: catalysts and templates coexist, and the two arms' low-fidelity fractions | |
| 38 | * are comparable. If they diverge sharply the comparison is confounded by mutation load. | |
| 39 | * R4 the advantage should GROW with base mu — the replicase matters most where templates decay fast. | |
| 40 | * | |
| 41 | * HONEST PRIOR: partial. The replicase is a public good inside its compartment, so free-riding should | |
| 42 | * bite exactly as it did in ribozyme.c — one level up. A null here is a real result. | |
| 43 | * | |
| 44 | * build: cc -O2 -Wall -std=c11 -o replicase replicase.c -lm | |
| 45 | */ | |
| 46 | #include <math.h> | |
| 47 | #include <stdio.h> | |
| 48 | #include <stdint.h> | |
| 49 | ||
| 50 | #define NFOLD 16 | |
| 51 | #define NPAIR (NFOLD/2) | |
| 52 | #define THETA 6 | |
| 53 | #define NTYPE 8 | |
| 54 | #define REPTYPE 0 /* the replicase type — excluded from the target set */ | |
| 55 | #define NTARG 3 /* target types, drawn from types 1..7 */ | |
| 56 | #define NCOMP 600 | |
| 57 | #define CSZ 8 | |
| 58 | #define GEN 200 | |
| 59 | #define BETA 2.0 | |
| 60 | #define PREAD 0.50 | |
| 61 | #define RLO 0.25 /* template mutation multiplier WITH a replicase present */ | |
| 62 | #define RHI 1.50 /* ... and without */ | |
| 63 | ||
| 64 | ||
| 65 | static unsigned long long RNG; | |
| 66 | static unsigned long long xs(void){ RNG^=RNG<<13; RNG^=RNG>>7; RNG^=RNG<<17; return RNG; } | |
| 67 | static double urand(void){ return (double)(xs()>>11)*(1.0/9007199254740992.0); } | |
| 68 | ||
| 69 | static int TARGET[NTYPE]; | |
| 70 | static int phi(uint32_t s){ int c=0; for(int i=0;i<NPAIR;i++){ int a=(s>>i)&1,b=(s>>(NFOLD-1-i))&1; if(a!=b)c++; } return c; } | |
| 71 | static int isfold(uint32_t s){ return phi(s)>=THETA; } | |
| 72 | static int typeof_(uint32_t s){ return (int)((s>>NFOLD)&7); } | |
| 73 | static uint32_t mut(uint32_t s,double mu){ for(int i=0;i<NFOLD+3;i++) if(urand()<mu) s^=(1u<<i); return s; } | |
| 74 | ||
| 75 | static uint32_t cell[NCOMP][CSZ], ncell[NCOMP][CSZ]; | |
| 76 | static double cum[NCOMP]; | |
| 77 | ||
| 78 | static int payoff_of(const uint32_t *c){ | |
| 79 | int seen[NTYPE]; for(int t=0;t<NTYPE;t++) seen[t]=0; | |
| 80 | int d=0; | |
| 81 | for(int k=0;k<CSZ;k++) if(isfold(c[k])){ int t=typeof_(c[k]); if(!seen[t]){ seen[t]=1; if(TARGET[t]) d++; } } | |
| 82 | return d; | |
| 83 | } | |
| 84 | static int has_replicase(const uint32_t *c){ | |
| 85 | for(int k=0;k<CSZ;k++) if(isfold(c[k]) && typeof_(c[k])==REPTYPE) return 1; | |
| 86 | return 0; | |
| 87 | } | |
| 88 | ||
| 89 | /* mode 0 = REPLICASE (fidelity conditioned on holding one), 1 = CONTROL (same structure, coin flip) */ | |
| 90 | static void run(int mode,double mu,double pflip,double *rep_freq,double *pay_out,double *tmpl,double *lofrac) | |
| 91 | { | |
| 92 | for(int t=0;t<NTYPE;t++) TARGET[t]=0; | |
| 93 | for(int got=0;got<NTARG;){ int t=1+(int)(xs()%(NTYPE-1)); if(!TARGET[t]){ TARGET[t]=1; got++; } } | |
| 94 | for(int c=0;c<NCOMP;c++) | |
| 95 | for(int k=0;k<CSZ;k++){ uint32_t v=0; for(int i=0;i<NFOLD+3;i++) if(urand()<0.5) v|=(1u<<i); cell[c][k]=v; } | |
| 96 | ||
| 97 | double losum=0; long lon=0; | |
| 98 | ||
| 99 | for(int g=0;g<GEN;g++){ | |
| 100 | for(int c=0;c<NCOMP;c++){ | |
| 101 | int lo_fid = (mode==0)? has_replicase(cell[c]) : (urand()<pflip); | |
| 102 | losum += lo_fid; lon++; | |
| 103 | double tmu = mu*(lo_fid?RLO:RHI); | |
| 104 | double tot=0,cw[CSZ]; | |
| 105 | for(int k=0;k<CSZ;k++){ tot += 1.0 + BETA*(1.0-(double)phi(cell[c][k])/NPAIR); cw[k]=tot; } | |
| 106 | int mach=-1; for(int k=0;k<CSZ;k++) if(isfold(cell[c][k])){ mach=k; break; } | |
| 107 | for(int k=0;k<CSZ;k++){ | |
| 108 | double r=urand()*tot; int lo=0,hi=CSZ-1; | |
| 109 | while(lo<hi){ int mid=(lo+hi)/2; if(cw[mid]<r) lo=mid+1; else hi=mid; } | |
| 110 | uint32_t par=cell[c][lo], child; double emu; | |
| 111 | if(!isfold(par)){ | |
| 112 | if(mach>=0 && urand()<PREAD){ /* READ: template -> catalyst */ | |
| 113 | child=(cell[c][mach]&((1u<<NFOLD)-1))|((uint32_t)typeof_(par)<<NFOLD); | |
| 114 | emu=mu; /* the new catalyst is a catalyst */ | |
| 115 | } else { child=par; emu=tmu; } /* COPY TRUE, at template fidelity */ | |
| 116 | } else { child=par; emu=mu; } /* catalyst -> catalyst */ | |
| 117 | ncell[c][k]=mut(child,emu); | |
| 118 | } | |
| 119 | } | |
| 120 | for(int c=0;c<NCOMP;c++) for(int k=0;k<CSZ;k++) cell[c][k]=ncell[c][k]; | |
| 121 | ||
| 122 | double tot=0; | |
| 123 | for(int c=0;c<NCOMP;c++){ tot += 0.05 + (double)payoff_of(cell[c]); cum[c]=tot; } | |
| 124 | for(int c=0;c<NCOMP;c++){ | |
| 125 | double r=urand()*tot; int lo=0,hi=NCOMP-1; | |
| 126 | while(lo<hi){ int mid=(lo+hi)/2; if(cum[mid]<r) lo=mid+1; else hi=mid; } | |
| 127 | for(int k=0;k<CSZ;k++) ncell[c][k]=cell[lo][(int)(xs()%CSZ)]; | |
| 128 | } | |
| 129 | for(int c=0;c<NCOMP;c++) for(int k=0;k<CSZ;k++) cell[c][k]=ncell[c][k]; | |
| 130 | } | |
| 131 | ||
| 132 | long nt=0; double rp=0,pay=0; | |
| 133 | for(int c=0;c<NCOMP;c++){ | |
| 134 | rp += has_replicase(cell[c]); pay += payoff_of(cell[c]); | |
| 135 | for(int k=0;k<CSZ;k++) if(!isfold(cell[c][k])) nt++; | |
| 136 | } | |
| 137 | *rep_freq=rp/NCOMP; *pay_out=pay/NCOMP; | |
| 138 | *tmpl=(double)nt/((double)NCOMP*CSZ); *lofrac=losum/(double)lon; | |
| 139 | } | |
| 140 | ||
| 141 | int main(void) | |
| 142 | { | |
| 143 | unsigned long long seeds[6]; | |
| 144 | seeds[0]=0x9e3779b97f4a7c15ULL; seeds[1]=0xd1b54a32d192ed03ULL; seeds[2]=0x2545f4914f6cdd1dULL; | |
| 145 | for(int i=3;i<6;i++) seeds[i]=0x9e3779b97f4a7c15ULL ^ (0x100000001b3ULL*(unsigned long long)(i+1)); | |
| 146 | int NS=6; | |
| 147 | ||
| 148 | printf("=== REPLICASE: can the genotype/phenotype loop close on itself? ===\n"); | |
| 149 | printf(" A type-%d catalyst improves TEMPLATE fidelity in its compartment (x%.2f vs x%.2f).\n",REPTYPE,RLO,RHI); | |
| 150 | printf(" It is EXCLUDED from the target set, so it pays nothing directly — pure overhead unless\n"); | |
| 151 | printf(" the template loop pays for itself. Not seeded, not protected; non-folders still replicate\n"); | |
| 152 | printf(" %.0fx faster, so the replicase is a PUBLIC GOOD inside its compartment and free-ridable.\n",1+BETA); | |
| 153 | printf(" CONTROL applies the identical two-rate structure by COIN FLIP instead — same mutation\n"); | |
| 154 | printf(" distribution, no type to select for. %d comps x %d, %d gens, %d seeds.\n\n",NCOMP,CSZ,GEN,NS); | |
| 155 | ||
| 156 | printf(" mu | REPLICASE: rep-freq / payoff / tmpl | CONTROL: rep-freq / payoff | lo-fid frac R/C\n"); | |
| 157 | printf(" ------+-------------------------------------+----------------------------+----------------\n"); | |
| 158 | double mus[]={0.005,0.01,0.02,0.04,0.08}; | |
| 159 | int nM=5, r1=0, r2=0, matched=1; double g0=0,g1=0; | |
| 160 | ||
| 161 | for(int i=0;i<nM;i++){ | |
| 162 | /* TWO PASS, so the control is matched rather than guessed: run the replicase arm, measure the | |
| 163 | fraction of compartment-generations that actually got the low rate, then give the control a | |
| 164 | coin flip at exactly that probability. The first version guessed PFLIP=0.60 from a uniform-type | |
| 165 | estimate; selection purges type 0 (it pays nothing) so the real figure is 0.03-0.13, and the | |
| 166 | control was running at a fraction of the mutation load. R3 caught it. */ | |
| 167 | double a1=0,a2=0,a3=0,a4=0, b1=0,b2=0,b4=0; | |
| 168 | double lf_meas[8]; | |
| 169 | for(int s=0;s<NS;s++){ | |
| 170 | double rf,pv,tm,lf; | |
| 171 | RNG=seeds[s]; run(0,mus[i],0.0,&rf,&pv,&tm,&lf); a1+=rf; a2+=pv; a3+=tm; a4+=lf; lf_meas[s]=lf; | |
| 172 | } | |
| 173 | for(int s=0;s<NS;s++){ | |
| 174 | double rf,pv,tm,lf; | |
| 175 | RNG=seeds[s]; run(1,mus[i],lf_meas[s],&rf,&pv,&tm,&lf); b1+=rf; b2+=pv; b4+=lf; | |
| 176 | } | |
| 177 | a1/=NS;a2/=NS;a3/=NS;a4/=NS; b1/=NS;b2/=NS;b4/=NS; | |
| 178 | printf(" %.3f | %.3f / %.2f / %.3f | %.3f / %.2f | %.2f / %.2f\n", | |
| 179 | mus[i],a1,a2,a3,b1,b2,a4,b4); | |
| 180 | if(a1>b1+0.02) r1++; | |
| 181 | if(a2>b2+0.05) r2++; | |
| 182 | if(fabs(a4-b4)>0.03) matched=0; | |
| 183 | if(i==0) g0=a2-b2; | |
| 184 | g1=a2-b2; | |
| 185 | } | |
| 186 | printf("\n rep-freq = fraction of compartments holding a replicase; payoff = target types present (max %d)\n\n",NTARG); | |
| 187 | ||
| 188 | printf(" R3 low-fidelity fractions comparable : %s\n", matched?"YES":"NO — CONFOUNDED by mutation load"); | |
| 189 | printf(" R1 replicase held above control : %d/%d mutation rates\n",r1,nM); | |
| 190 | printf(" R2 payoff higher than control : %d/%d mutation rates\n",r2,nM); | |
| 191 | printf(" R4 advantage grows with mu : %+.2f at mu=%.3f -> %+.2f at mu=%.3f\n\n",g0,mus[0],g1,mus[nM-1]); | |
| 192 | ||
| 193 | if(!matched) | |
| 194 | printf(" => CONFOUNDED. The arms did not experience comparable mutation loads, so any difference\n" | |
| 195 | " is the load and not the loop.\n"); | |
| 196 | else if(r1>=nM-1 && r2>=nM-1) | |
| 197 | printf(" => THE LOOP CLOSED. Lineages hold a catalyst type that pays them NOTHING directly,\n" | |
| 198 | " because it protects the templates that carry everything that does pay — and they\n" | |
| 199 | " carry more function for it than the matched control. Genotype and phenotype became\n" | |
| 200 | " mutually dependent without either being rewarded for it. That is rung 1->2's\n" | |
| 201 | " architecture, bootstrapped rather than installed.\n"); | |
| 202 | else if(r2>=nM-1) | |
| 203 | printf(" => FUNCTION UP, REPLICASE NOT HELD (%d/%d vs %d/%d). The fidelity boost helps where it\n" | |
| 204 | " happens to occur, but lineages do not retain the replicase — free-riding inside the\n" | |
| 205 | " compartment wins, exactly as ribozyme.c found one level down. The loop does not close.\n",r2,nM,r1,nM); | |
| 206 | else | |
| 207 | printf(" => HONEST NULL. The replicase is not held and function is not raised. A catalyst that\n" | |
| 208 | " pays only by protecting templates cannot pay for itself here: it is a public good\n" | |
| 209 | " inside its own compartment and is free-ridden away. The genotype/phenotype loop\n" | |
| 210 | " needs something this model does not have.\n"); | |
| 211 | return 0; | |
| 212 | } |
| … | … | @@ -0,0 +1,72 @@ |
| 1 | # `replicase.c` — the loop does not close, and the wall is the same one, one level up | |
| 2 | ||
| 3 | The last experiment of the arc, and the first with **genotype and phenotype mutually dependent** | |
| 4 | rather than one being a convenience for the other. | |
| 5 | ||
| 6 | ## What it fixes from `genome.c` | |
| 7 | ||
| 8 | Two flaws, both found by the earlier runs: | |
| 9 | ||
| 10 | 1. **Reading was a dead end.** A picked template always became a catalyst, so a template could be | |
| 11 | useful or persist, never both. Now it is read with probability `PREAD` and otherwise **copied | |
| 12 | true**, exactly as a catalyst is. Reading is a function templates provide, not a route by which | |
| 13 | they propagate. | |
| 14 | 2. **Template copying was free physics.** `child = par`, perfect, no cost. In reality copying a | |
| 15 | genome needs a **replicase** — itself a catalyst, itself specified by a template. That is the loop. | |
| 16 | ||
| 17 | So: a type-0 catalyst improves template fidelity in its compartment (×0.25 vs ×1.50). It is | |
| 18 | **excluded from the target set**, so it pays nothing directly — pure overhead unless the loop pays | |
| 19 | for itself. Not seeded, not protected; non-folders still replicate 3× faster, so the replicase is a | |
| 20 | **public good inside its own compartment**, free-ridable exactly as catalysis was in `ribozyme.c`. | |
| 21 | ||
| 22 | ## The control had to be fixed first | |
| 23 | ||
| 24 | R3 failed on the first run: the replicase arm got low-fidelity 3–13% of the time, the control **60%**. | |
| 25 | I had guessed `PFLIP` from a uniform-type estimate, but selection *purges* type 0 precisely because it | |
| 26 | pays nothing, so the real frequency is an order of magnitude lower — and the control was running at a | |
| 27 | fraction of the mutation load, against the arm it was meant to test. Fixed by a two-pass design: run | |
| 28 | the replicase arm, measure the realized low-fidelity fraction per seed, then give the control a coin | |
| 29 | flip at exactly that probability. | |
| 30 | ||
| 31 | ## Results (6 seeds, matched) | |
| 32 | ||
| 33 | | μ | replicase: rep-freq / payoff / tmpl | control: rep-freq / payoff | lo-fid R / C | | |
| 34 | |---|---|---|---| | |
| 35 | | 0.005 | 0.025 / 1.56 / 0.094 | 0.022 / 1.57 | 0.03 / 0.03 | | |
| 36 | | 0.010 | 0.039 / 1.51 / 0.145 | 0.050 / 1.51 | 0.04 / 0.04 | | |
| 37 | | 0.020 | 0.060 / 1.44 / 0.240 | 0.094 / 1.44 | 0.07 / 0.07 | | |
| 38 | | 0.040 | 0.111 / 1.37 / 0.361 | 0.100 / 1.39 | 0.10 / 0.10 | | |
| 39 | | 0.080 | 0.129 / 1.22 / 0.502 | 0.129 / 1.23 | 0.13 / 0.13 | | |
| 40 | ||
| 41 | **R3 passes** — loads matched exactly. **R1: 0/5.** **R2: 0/5.** The two arms are indistinguishable | |
| 42 | on both measures. (Replicase frequency rises with μ in *both* arms — that is chance generating more | |
| 43 | type-0 catalysts, not selection retaining them.) | |
| 44 | ||
| 45 | > **HONEST NULL.** A catalyst that pays only by protecting templates cannot pay for itself here. | |
| 46 | ||
| 47 | ## Why — and it is the arc's own wall, one level up | |
| 48 | ||
| 49 | The replicase improves fidelity for **every** template in its compartment, including those of | |
| 50 | free-riders, and it costs a slot: it occupies one of eight without contributing to payoff. | |
| 51 | Within-compartment selection therefore purges it, and between-compartment selection cannot retain it | |
| 52 | because its benefit is indirect and shared. | |
| 53 | ||
| 54 | That is precisely `ribozyme.c`'s public-goods wall, relocated. There, catalysis was the public good | |
| 55 | and a compartment privatised it. Here **the compartment cannot privatise the replicase, because the | |
| 56 | compartment is exactly the scope over which the replicase's benefit is shared.** A boundary can only | |
| 57 | privatise a good it can exclude non-contributors from — and everything inside a compartment is inside | |
| 58 | it. | |
| 59 | ||
| 60 | I predicted partial. It was a complete null; free-riding bit harder than expected. | |
| 61 | ||
| 62 | ## What this leaves | |
| 63 | ||
| 64 | `fidfloor.c` says the genotype/phenotype split is **necessary**. `genome.c` says the opportunity is | |
| 65 | **not sufficient**. `replicase.c` says the loop **does not close** when the replicase is a | |
| 66 | compartment-scale public good. Three negatives that between them locate the problem precisely: | |
| 67 | ||
| 68 | > The replicase must be privatised at a scale *smaller* than the compartment — coupled to the | |
| 69 | > particular templates it copies rather than to everything nearby. Which is what a replisome does, | |
| 70 | > and what physical association buys that diffusion does not. | |
| 71 | ||
| 72 | That is the next design, and it is unbuilt. |