| … | … | @@ -0,0 +1,183 @@ |
| 1 | /* substrate.c — ONE representation for all five rungs. Milestone 1: does it express rung 1 and 2? |
| 2 | * |
| 3 | * WHY. SYNTHESIS's standing admission is that no rung ever produced the next, and the cold read of |
| 4 | * Part C found why it is structural rather than accidental: EVERY RUNG HAS A DIFFERENT SUBSTRATE. |
| 5 | * Bit strings, count vectors, reaction sets, gene-type lists, role counts. A rung's output is not |
| 6 | * expressible as the next rung's input, so the ladder can only ever be narrated across disconnected |
| 7 | * models. rafcomp.c got two rungs co-present by hand-translating one into the other's terms; doing |
| 8 | * that for a whole ladder means one representation that expresses all of them. |
| 9 | * |
| 10 | * THE CONSTRAINT TONIGHT ADDED. genome.c, replicase.c and replisome.c all failed for one reason: |
| 11 | * nothing in those models was expensive enough to be worth remembering, because payoff was scored on |
| 12 | * the present configuration and any configuration was cheap to rediscover. linkage.c, the one that |
| 13 | * worked, is the one whose payoff required a COMBINATION. So the substrate must make combinations |
| 14 | * expensive to rediscover, or memory can never pay in it. |
| 15 | * |
| 16 | * THE CHOICE. A reaction fires only when a SPECIFIC SET of species is present — reactants AND a |
| 17 | * catalyst. That is combinatorial by construction, and it already covers the ladder: |
| 18 | * rung 1 a self-sustaining reaction subset (a RAF) |
| 19 | * rung 2 a template: the reaction A -> A + A catalysed by A |
| 20 | * rung 3 species ARE sequences; mutation is a variant-producing reaction |
| 21 | * rung 4 a container is a sub-multiset copied as a unit — AND IS ITSELF A SPECIES |
| 22 | * rung 5 a specialist is a species participating in only part of the reaction set |
| 23 | * The recursive move is the last-but-one: containers are species. That is what lets a rung's output |
| 24 | * be the next rung's input instead of being hand-translated. |
| 25 | * |
| 26 | * MILESTONE 1 (this file) — the test of the test. A new substrate is worthless if it does not |
| 27 | * faithfully express what we already have, so before anything else: |
| 28 | * S1 the engine must REPRODUCE rung1.c's percolation threshold. Same construction, same sweep. If |
| 29 | * the curve differs, the substrate does not express rung 1 and nothing built on it can be |
| 30 | * trusted. |
| 31 | * S2 a self-templating species (rung 2) must be EXPRESSIBLE in the same engine with no special |
| 32 | * case — just a reaction whose product is its own catalyst — and must self-sustain when its |
| 33 | * inputs are food. |
| 34 | * S3 both must be able to coexist in ONE network, which is the whole point: it is the precondition |
| 35 | * for rung 2 arising INSIDE a rung-1 system rather than being a separate model. |
| 36 | * Milestones 2+ (containers-as-species, then rungs 3-5) are not attempted here. A substrate that |
| 37 | * fails S1 is not worth extending. |
| 38 | * |
| 39 | * build: cc -O2 -Wall -std=c11 -o substrate substrate.c -lm |
| 40 | */ |
| 41 | #include <math.h> |
| 42 | #include <stdio.h> |
| 43 | #include <string.h> |
| 44 | |
| 45 | #define NSPEC 60 /* species; rung1.c used N=60 */ |
| 46 | #define NFOOD 8 /* food species, always present; rung1.c used NF=8 */ |
| 47 | #define NRXN 300 /* reactions; rung1.c used M=300 */ |
| 48 | #define INST 60 /* network instances per point; rung1.c used INST=60 */ |
| 49 | |
| 50 | static unsigned long long RNG; |
| 51 | static unsigned long long xs(void){ RNG^=RNG<<13; RNG^=RNG>>7; RNG^=RNG<<17; return RNG; } |
| 52 | static double urand(void){ return (double)(xs()>>11)*(1.0/9007199254740992.0); } |
| 53 | static int rint_(int n){ return (int)(xs()%(unsigned)n); } |
| 54 | |
| 55 | /* ---- THE TYPE. A reaction is (reactant a, reactant b) -> product, enabled by any catalyst. |
| 56 | Species are integers. Everything in the ladder is meant to be expressed as species+reactions: |
| 57 | a template is a reaction whose product catalyses itself; a container will be a species whose |
| 58 | contents are a multiset (milestone 2). ---- */ |
| 59 | typedef struct { int a, b, prod; unsigned char cat[NSPEC]; } Rxn; |
| 60 | static Rxn R[NRXN]; |
| 61 | static int nrxn; |
| 62 | static unsigned char present[NSPEC], active[NRXN]; |
| 63 | |
| 64 | static void net_clear(void){ nrxn=0; } |
| 65 | static int net_add(int a,int b,int prod){ |
| 66 | if(nrxn>=NRXN) return -1; |
| 67 | R[nrxn].a=a; R[nrxn].b=b; R[nrxn].prod=prod; |
| 68 | memset(R[nrxn].cat,0,sizeof R[nrxn].cat); |
| 69 | return nrxn++; |
| 70 | } |
| 71 | static void net_catalyse(int j,int sp){ R[j].cat[sp]=1; } |
| 72 | |
| 73 | /* rung1.c's construction, verbatim in shape: random reactions, each species catalyses each |
| 74 | reaction with probability p. */ |
| 75 | static void net_random_n(double p,int count){ |
| 76 | net_clear(); |
| 77 | if(count>NRXN) count=NRXN; |
| 78 | for(int j=0;j<count;j++){ |
| 79 | int id=net_add(rint_(NSPEC),rint_(NSPEC),NFOOD+rint_(NSPEC-NFOOD)); |
| 80 | if(id<0) break; |
| 81 | for(int m=0;m<NSPEC;m++) if(urand()<p) net_catalyse(id,m); |
| 82 | } |
| 83 | } |
| 84 | static void net_random(double p){ net_random_n(p,NRXN); } |
| 85 | |
| 86 | /* maxRAF by prune-to-fixpoint — rung1.c's algorithm, operating on the substrate's own type. */ |
| 87 | static int max_raf(void){ |
| 88 | for(int j=0;j<nrxn;j++) active[j]=1; |
| 89 | for(;;){ |
| 90 | for(int m=0;m<NSPEC;m++) present[m]=(m<NFOOD)?1:0; |
| 91 | int grew=1; |
| 92 | while(grew){ grew=0; |
| 93 | for(int j=0;j<nrxn;j++){ if(!active[j]) continue; |
| 94 | if(!present[R[j].a]||!present[R[j].b]||present[R[j].prod]) continue; |
| 95 | int cz=0; for(int m=0;m<NSPEC;m++) if(R[j].cat[m]&&present[m]){cz=1;break;} |
| 96 | if(cz){ present[R[j].prod]=1; grew=1; } |
| 97 | } |
| 98 | } |
| 99 | int changed=0; |
| 100 | for(int j=0;j<nrxn;j++){ if(!active[j]) continue; |
| 101 | int sup=present[R[j].a]&&present[R[j].b]; |
| 102 | int cz=0; for(int m=0;m<NSPEC;m++) if(R[j].cat[m]&&present[m]){cz=1;break;} |
| 103 | if(!(sup&&cz)){ active[j]=0; changed=1; } |
| 104 | } |
| 105 | if(!changed) break; |
| 106 | } |
| 107 | int n=0; for(int j=0;j<nrxn;j++) n+=active[j]; |
| 108 | return n; |
| 109 | } |
| 110 | |
| 111 | int main(void) |
| 112 | { |
| 113 | printf("=== SUBSTRATE, MILESTONE 1: does one representation express rung 1 AND rung 2? ===\n"); |
| 114 | printf(" Species are integers; a reaction is (a,b)->prod enabled by any present catalyst.\n"); |
| 115 | printf(" Nothing is special-cased per rung. %d species (%d food), %d reactions.\n\n",NSPEC,NFOOD,NRXN); |
| 116 | |
| 117 | /* ---- S1: reproduce rung1.c's percolation threshold ---- */ |
| 118 | printf(" S1 — reproduce rung1.c's threshold (its published curve in the right column)\n"); |
| 119 | printf(" %-8s %10s %10s %s\n","p","maxRAF","P(RAF>0)","rung1.c"); |
| 120 | const char *ref[6]={"~0 (empty)","~0","rising","rising","system-spanning","system-spanning"}; |
| 121 | double ps[6]={0.006,0.012,0.021,0.030,0.042,0.054}; |
| 122 | int ok_low=0, ok_high=0; |
| 123 | for(int i=0;i<6;i++){ |
| 124 | double sz=0; int nz=0; |
| 125 | for(int t=0;t<INST;t++){ |
| 126 | RNG=0x9e3779b97f4a7c15ULL + (unsigned long long)(i*10007+t)*0x2545F4914F6CDD1DULL; |
| 127 | net_random(ps[i]); int s=max_raf(); sz+=s; if(s>0) nz++; |
| 128 | } |
| 129 | double m=sz/INST, f=(double)nz/INST; |
| 130 | printf(" %-8.4f %10.1f %10.2f %s\n",ps[i],m,f,ref[i]); |
| 131 | if(i==0 && m<1.0) ok_low=1; |
| 132 | if(i==5 && m>20.0) ok_high=1; |
| 133 | } |
| 134 | printf(" => %s\n\n", (ok_low&&ok_high)? |
| 135 | "MATCHES: empty below, system-spanning above — rung 1 is expressed faithfully." |
| 136 | :"DIVERGES from rung1.c — the substrate does not express rung 1. Stop here."); |
| 137 | |
| 138 | /* ---- S2: a template is just a reaction that catalyses itself. No special case. ---- */ |
| 139 | printf(" S2 — express rung 2 (a template) in the SAME type: a reaction whose product is its\n"); |
| 140 | printf(" own catalyst. Food-only reactants, so it needs nothing but the food set.\n"); |
| 141 | net_clear(); |
| 142 | int T=NFOOD; /* the template species */ |
| 143 | int t1=net_add(0,1,T); net_catalyse(t1,T); /* food + food -> T, catalysed by T */ |
| 144 | int self=max_raf(); |
| 145 | printf(" template alone, uncatalysed start : maxRAF %d (expected 0 — needs a seed)\n",self); |
| 146 | net_catalyse(t1,0); /* also catalysed by a food species = a seed */ |
| 147 | int seeded=max_raf(); |
| 148 | printf(" with a food-species seed : maxRAF %d (expected 1 — self-sustains)\n",seeded); |
| 149 | int s2 = (self==0 && seeded==1); |
| 150 | printf(" => %s\n\n", s2? |
| 151 | "EXPRESSED: rung 2 is a reaction shape, not a new model. And it reproduces the known\n bootstrap problem — a self-catalysing template cannot start itself without a seed." |
| 152 | :"NOT EXPRESSED as expected — check the encoding before building on it."); |
| 153 | |
| 154 | /* ---- S3: both in ONE network, which is the precondition for chaining ---- */ |
| 155 | printf(" S3 — rung 1 and rung 2 in ONE network (the point: rung 2 could arise INSIDE rung 1)\n"); |
| 156 | int both=0, tin=0; |
| 157 | { |
| 158 | /* BUG FIXED: net_random filled all NRXN slots, so the following net_add returned -1 and |
| 159 | active[-1] was read — undefined behaviour, reported as "no". Leave a slot free. */ |
| 160 | int hits=0, tries=8; |
| 161 | for(int t=0;t<tries;t++){ |
| 162 | RNG=0xfeedface12345678ULL + (unsigned long long)t*0x9E3779B97F4A7C15ULL; |
| 163 | net_random_n(0.030,NRXN-1); |
| 164 | int j=net_add(0,1,T); |
| 165 | if(j<0){ printf(" reaction table full — cannot express both\n"); break; } |
| 166 | net_catalyse(j,T); net_catalyse(j,0); |
| 167 | int n=max_raf(); |
| 168 | if(active[j] && n>1) hits++; |
| 169 | if(t==0) printf(" mixed network maxRAF %d, template reaction in the RAF: %s\n",n,active[j]?"YES":"no"); |
| 170 | } |
| 171 | printf(" across %d seeds, template retained in the RAF: %d\n",tries,hits); |
| 172 | tin=(hits>0); both=(hits>=tries-1); |
| 173 | } |
| 174 | printf(" => %s\n\n", both? |
| 175 | "COEXIST: a self-templating species sits inside an autocatalytic set, in one type, with no\n translation between them. That is the precondition the ladder has never had." |
| 176 | :"did NOT coexist in this instance — retry across seeds before concluding."); |
| 177 | |
| 178 | printf(" MILESTONE 1 %s\n", (ok_low&&ok_high&&s2&&both)?"PASSED":"NOT PASSED"); |
| 179 | printf(" Next, and NOT attempted here: containers as species (rung 4), which is the recursive\n"); |
| 180 | printf(" move that lets a compartment be produced, counted and selected like anything else —\n"); |
| 181 | printf(" and is what would let a rung's output actually BE the next rung's input.\n"); |
| 182 | return 0; |
| 183 | } |