OverviewFilesCommitsIssuesDocsReleasesPeers

basal-agent commits / 44a714d

basal-agent: chemoton.c — compartment-level selection EMERGES, no decree
byproduct.c showed isolation is not compartmentalization: supplied walls reach 5.65, supplied walls + between-site selection reach 10.78. The active ingredient is that compartments DIVIDE and are SELECTED — and that step was DECREED in both ribozyme.c and byproduct.c ("sample a parent site proportional to mean phi"). Same idea moved to the other half: a compartment divides when it grows, so differential reproduction reduces to differential GROWTH, and if wall material comes from the chemistry inside then growth is automatically a function of catalytic content. catalysis -> uptake -> growth -> division. Plus the ingredient byproduct.c lacked: material is CONSERVED (6000, verified exactly every cell) and rationed, so compartments compete. No group-selection step exists in the code; the within-compartment tragedy is untouched. 6 seeds, start phi 5.99 (= neutral): divsz | COUPLED | CONTROL | comps | mean sz | div/gen | ribozyme (decreed) 4 | 11.68 | 6.36 | 2317 | 1.87 | 100.7 | 11.64 8 | 11.45 | 6.00 | 1397 | 3.41 | 23.4 | 11.34 16 | 7.65 | 5.76 | 798 | 6.34 | 5.7 | 5.90 32 | 5.86 | 5.62 | 426 | 12.25 | 1.2 | 5.73 C5 passed (real turnover), C4 passed (control blind to phi stays at/below neutral in every row — division+assortment alone does nothing, the growth coupling does the work), C2 passed (phi 5.99 -> 11.68). C3 the key test: PARTIAL by the pre-registered bar. The threshold reappeared and three of four points land on ribozyme almost exactly (11.68/11.64, 11.45/11.34, 5.86/5.73), but divsz 16 gives 7.65 where the decree gave 5.90 — a soft shoulder, not a cliff. Scores small 2/2, large 1/2. Caveat, post-hoc and not a rescue: the size axes differ, since compartments here die back continuously so realized mean size is ~half the threshold (divsz 16 = ~6-unit compartments, inside ribozyme's working range). Verdict stays PARTIAL. ESTABLISHES: compartment-level differential reproduction need not be supplied. Answers the question byproduct.c opened. DOES NOT establish rung 1->2: this is still the fidelity/replicase half, phi is a supplied fold->catalysis map, and what rises is catalytic quality not open-ended heredity. The compartment now builds and reproduces itself; the template still does not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
44a714d3cb849e1e79314b46fc4b98f1d34e1fbc
by plankatron <fleeky@gmail.com> · 2026-07-31T23:18:01.000Z

Changed files (3) +323 -0

.gitignore +1 -0
……@@ -43,3 +43,4 @@ gardphi
4343signal
4444linkage
4545byproduct
46chemoton
chemoton.c +249 -0
……@@ -0,0 +1,249 @@
1/* chemoton.c — compartments that GROW from their own chemistry's output. Does the stochastic
2 * corrector appear WITHOUT anyone decreeing group selection?
3 *
4 * WHERE THIS COMES FROM. byproduct.c applied the byproduct idea to the wrong half of what a
5 * compartment is. It coupled catalysis -> wall HEIGHT -> isolation, and measured that isolation
6 * alone does nothing: supplied walls at full height reach phi 5.65, no better than open space, while
7 * supplied walls PLUS between-site selection reach 10.78. So the barrier was never the active
8 * ingredient. What made compartments work is that they DIVIDE and are SELECTED on catalytic content
9 * -- and in ribozyme.c and byproduct.c that step was DECREED ("sample a parent site proportional to
10 * its mean phi"), never derived.
11 *
12 * THE FIX IS THE SAME IDEA, MOVED. A compartment divides when it grows big enough, so differential
13 * reproduction reduces to differential GROWTH. If the wall material is produced by the chemistry
14 * inside, growth rate is automatically a function of internal catalytic content:
15 * catalysis -> membrane material -> growth -> division
16 * A parasite produces no material, so a parasite-heavy compartment grows slowly and divides rarely.
17 * That yields the decreed rule as a CONSEQUENCE rather than an assertion. (This is Ganti's chemoton
18 * / the Szostak protocell coupling -- the field's standing answer, not a new idea. What is new here
19 * is testing it against our own measured claim that isolation is not enough.)
20 *
21 * AND A SECOND INGREDIENT byproduct.c LACKED: something LIMITING. There, sites were fixed at 600, so
22 * a dissolving region freed nothing and a thriving one had nowhere to expand. Decay without
23 * competition is not selection. Here MATERIAL IS CONSERVED: pool + compartment stores + units*cost
24 * is constant, uptake is rationed when demand exceeds supply, and dead units return their material.
25 * Compartments genuinely compete.
26 *
27 * WHAT IS SUPPLIED vs WHAT MUST EMERGE:
28 * SUPPLIED: the physical coupling activity -> material uptake, a division size, a per-unit death
29 * rate, and a conserved material pool. The division size is SWEPT, not picked.
30 * MUST EMERGE: differential reproduction of compartments. NOTHING samples parents by fitness.
31 * Compartments divide because they grew; they grew because they took up material; they took up
32 * material because their contents catalyzed. No group-selection step exists anywhere in the code.
33 * UNTOUCHED: the within-compartment tragedy. Parasites still replicate 3x faster and still take the
34 * growth slots, so a fast-growing compartment fills with the parasites its own growth carries.
35 *
36 * Chemistry identical to ribozyme.c / coupling.c / partition.c / signal.c / byproduct.c
37 * (phi = hairpin fold, parasites 3x faster, F0=0.85, F1=0.999, start phi ~5.99 = the neutral
38 * expectation 6.0). Reference: ribozyme well-mixed 5.99 -> 5.50; DECREED group selection at
39 * compartment size 8 -> 11.34, and it FAILS at size >= 16.
40 *
41 * PRE-REGISTERED:
42 * C1 the within-compartment tragedy still runs (parasites take growth slots) -- stated so that a
43 * rise cannot be explained by having quietly removed the problem.
44 * C2 phi RISES anyway, because catalyst-rich compartments divide faster. The corrector, emergent.
45 * C3 THE KEY TEST: the SIZE THRESHOLD reappears on its own. Sweeping division size, crossing at
46 * small sizes and failure at large ones -- matching ribozyme.c's decreed-selection result
47 * (works <= 8, fails >= 16). If growth-coupling really is the mechanism behind the decree, it
48 * must reproduce the decree's signature, not merely produce some rise.
49 * C4 CONTROL: uptake CONSTANT PER UNIT, independent of phi. Identical division, assortment, death
50 * and material limit; only the growth->catalysis coupling is cut. If phi rises here too, the
51 * effect is division-plus-assortment per se and C2/C3 mean nothing.
52 * C5 TEST OF THE TEST: do compartments actually divide and die, and does the population turn over?
53 * Report divisions and deaths per generation and the compartment count. If nothing divides, no
54 * compartment-level reproduction happened and a null says nothing.
55 *
56 * Do not tune to force a positive. A null with C5 passing is a real result: it would say the
57 * coupling is insufficient and the first unit of selection had to be supplied by the world.
58 *
59 * build: cc -O2 -Wall -std=c11 -o chemoton chemoton.c -lm
60 */
61#include <math.h>
62#include <stdio.h>
63#include <stdint.h>
64#include <string.h>
65
66#define L 24
67#define PHIMAX (L/2)
68#define F0 0.85
69#define F1 0.999
70#define BETA 2.0
71#define GEN 300
72
73#define MAXCOMP 3000
74#define MAXSZ 48
75#define COSTUNIT 1.0
76#define TOTALMAT 6000.0 /* conserved: pool + stores + units*COSTUNIT */
77#define UPTAKE 0.55
78#define DEATH 0.10
79
80static unsigned long long RNG;
81static unsigned long long xs(void){ RNG^=RNG<<13; RNG^=RNG>>7; RNG^=RNG<<17; return RNG; }
82static double urand(void){ return (double)(xs()>>11)*(1.0/9007199254740992.0); }
83
84static int phi(uint32_t s){ int c=0; for(int i=0;i<PHIMAX;i++){ int a=(s>>i)&1,b=(s>>(L-1-i))&1; if(a!=b)c++; } return c; }
85static uint32_t replicate(uint32_t t,double f){ uint32_t c=0; for(int i=0;i<L;i++){ int b=(t>>i)&1; int nb=(urand()<f)?(1-b):b; if(nb)c|=(1u<<i);} return c; }
86
87typedef struct { uint32_t u[MAXSZ]; int n; double mat; } Comp;
88static Comp C[MAXCOMP];
89static int ncomp;
90static double pool;
91static double demand[MAXCOMP];
92
93/* coupled=1: uptake tracks catalytic output. coupled=0: uptake is constant per unit (C4 control). */
94static double run(int coupled,int divsz,double *phi_start,double *ndiv,double *ndeath,
95 double *ncomp_end,double *msize,double *matcheck)
96{
97 int seed_n = divsz/2 > 2 ? divsz/2 : 2;
98 ncomp = 600;
99 for(int c=0;c<ncomp;c++){
100 C[c].n=seed_n; C[c].mat=0.0;
101 for(int k=0;k<seed_n;k++){ uint32_t v=0; for(int b=0;b<L;b++) if(urand()<0.5) v|=(1u<<b); C[c].u[k]=v; }
102 }
103 { double sp=0; long t=0; for(int c=0;c<ncomp;c++) for(int k=0;k<C[c].n;k++){ sp+=phi(C[c].u[k]); t++; } *phi_start=sp/t; }
104 { long units=0; for(int c=0;c<ncomp;c++) units+=C[c].n; pool = TOTALMAT - units*COSTUNIT; }
105
106 double totdiv=0, totdeath=0;
107
108 for(int g=0;g<GEN;g++){
109 /* ---- uptake: demand from catalytic output, RATIONED when the pool cannot meet it ---- */
110 double D=0;
111 for(int c=0;c<ncomp;c++){
112 double out=0;
113 if(coupled){ for(int k=0;k<C[c].n;k++) out += (double)phi(C[c].u[k])/PHIMAX; }
114 else out = 0.5*C[c].n; /* control: same scale, blind to phi */
115 demand[c]=UPTAKE*out; D+=demand[c];
116 }
117 double frac = (D>0 && D>pool)? pool/D : 1.0;
118 for(int c=0;c<ncomp;c++){ double take=demand[c]*frac; C[c].mat+=take; pool-=take; }
119
120 /* ---- growth: spend material on new units. Parasites still take the slots (C1). ---- */
121 for(int c=0;c<ncomp;c++){
122 if(C[c].n<=0) continue;
123 double a=0; for(int k=0;k<C[c].n;k++) a+=phi(C[c].u[k]);
124 double f=F0+(F1-F0)*((a/C[c].n)/PHIMAX);
125 while(C[c].mat>=COSTUNIT && C[c].n<MAXSZ){
126 double tot=0,cum[MAXSZ];
127 for(int k=0;k<C[c].n;k++){ tot+=1.0+BETA*(1.0-(double)phi(C[c].u[k])/PHIMAX); cum[k]=tot; }
128 double r=urand()*tot; int lo=0,hi=C[c].n-1;
129 while(lo<hi){ int mid=(lo+hi)/2; if(cum[mid]<r) lo=mid+1; else hi=mid; }
130 C[c].u[C[c].n]=replicate(C[c].u[lo],f); C[c].n++; C[c].mat-=COSTUNIT;
131 }
132 }
133
134 /* ---- per-unit death: material returns to the pool ---- */
135 for(int c=0;c<ncomp;c++){
136 for(int k=0;k<C[c].n;){
137 if(urand()<DEATH){ C[c].u[k]=C[c].u[C[c].n-1]; C[c].n--; pool+=COSTUNIT; }
138 else k++;
139 }
140 }
141
142 /* ---- division at threshold: random assortment, material split ---- */
143 int base=ncomp;
144 for(int c=0;c<base;c++){
145 if(C[c].n>=divsz && ncomp<MAXCOMP){
146 Comp *p=&C[c], *q=&C[ncomp];
147 q->n=0; q->mat=p->mat*0.5; p->mat*=0.5;
148 int w=0;
149 for(int k=0;k<p->n;k++){
150 if(urand()<0.5) q->u[q->n++]=p->u[k];
151 else p->u[w++]=p->u[k];
152 }
153 p->n=w; ncomp++; totdiv++;
154 }
155 }
156
157 /* ---- compartment death when empty: stored material returns ---- */
158 for(int c=0;c<ncomp;){
159 if(C[c].n<=0){ pool+=C[c].mat; C[c]=C[ncomp-1]; ncomp--; totdeath++; }
160 else c++;
161 }
162 if(ncomp<=0) break;
163 }
164
165 double sp=0; long t=0;
166 for(int c=0;c<ncomp;c++) for(int k=0;k<C[c].n;k++){ sp+=phi(C[c].u[k]); t++; }
167 double stored=0; for(int c=0;c<ncomp;c++) stored+=C[c].mat;
168 *ndiv=totdiv/GEN; *ndeath=totdeath/GEN; *ncomp_end=ncomp;
169 *msize = ncomp? (double)t/ncomp : 0;
170 *matcheck = pool+stored+t*COSTUNIT;
171 return t? sp/t : 0.0;
172}
173
174int main(void)
175{
176 unsigned long long seeds[6];
177 seeds[0]=0x9e3779b97f4a7c15ULL; seeds[1]=0xd1b54a32d192ed03ULL; seeds[2]=0x2545f4914f6cdd1dULL;
178 for(int i=3;i<6;i++) seeds[i]=0x9e3779b97f4a7c15ULL ^ (0x100000001b3ULL*(unsigned long long)(i+1));
179 int NS=6;
180
181 printf("=== CHEMOTON: compartments that grow from their own chemistry's output ===\n");
182 printf(" catalysis -> material uptake -> growth -> division. NOTHING samples parents by fitness;\n");
183 printf(" no group-selection step exists in the code. Material is CONSERVED (%.0f) and rationed,\n",TOTALMAT);
184 printf(" so compartments compete. Parasites still replicate %.0fx faster and still take growth slots.\n",1+BETA);
185 printf(" REFERENCE: ribozyme, with DECREED group selection, crosses at size 8 (11.34) and FAILS\n");
186 printf(" at size >= 16. If growth-coupling is the mechanism behind that decree, the SIZE THRESHOLD\n");
187 printf(" must reappear here on its own.\n\n");
188
189 printf(" div size | COUPLED phi | CONTROL phi | comps | mean sz | div/gen | death/gen | material\n");
190 printf(" ---------+-------------+-------------+-------+---------+---------+-----------+---------\n");
191
192 int sizes[]={4,8,16,32};
193 int NZ=(int)(sizeof(sizes)/sizeof(sizes[0]));
194 double start=0; int turned_over=0;
195 double co[8], ct[8];
196
197 for(int z=0;z<NZ;z++){
198 double ec=0,ek=0,nd=0,nx=0,nc=0,ms=0,mc=0,ps=0;
199 for(int s=0;s<NS;s++){
200 double p,d,x,n,m,mm;
201 RNG=seeds[s]; ec+=run(1,sizes[z],&p,&d,&x,&n,&m,&mm); nd+=d; nx+=x; nc+=n; ms+=m; mc+=mm; ps+=p;
202 RNG=seeds[s]; ek+=run(0,sizes[z],&p,&d,&x,&n,&m,&mm);
203 }
204 ec/=NS;ek/=NS;nd/=NS;nx/=NS;nc/=NS;ms/=NS;mc/=NS;ps/=NS;
205 start=ps; co[z]=ec; ct[z]=ek;
206 printf(" %2d | %5.2f | %5.2f | %5.0f | %5.2f | %6.1f | %6.1f | %.0f\n",
207 sizes[z],ec,ek,nc,ms,nd,nx,mc);
208 if(nd>1.0 && nx>1.0) turned_over=1;
209 }
210 printf("\n start phi (= neutral expectation) %.2f [material must stay ~%.0f]\n\n",start,TOTALMAT);
211
212 int small_wins=0, large_fails=0, ctrl_flat=1;
213 for(int z=0;z<NZ;z++){
214 if(sizes[z]<=8 && co[z]>start+0.5) small_wins++;
215 if(sizes[z]>=16 && co[z]<start+0.5) large_fails++;
216 if(ct[z]>start+0.5) ctrl_flat=0;
217 }
218
219 printf(" C5 compartments divided AND died (turnover) : %s\n", turned_over?"YES":"NO — test did not run");
220 printf(" C2 phi rose above neutral somewhere : %s\n", (co[0]>start+0.5||co[1]>start+0.5||co[2]>start+0.5||co[3]>start+0.5)?"YES":"NO");
221 printf(" C3 SIZE THRESHOLD reappeared (small yes/large no): small %d/2, large %d/2\n", small_wins, large_fails);
222 printf(" C4 control (uptake blind to phi) stayed flat : %s\n\n", ctrl_flat?"YES":"NO — CONFOUNDED");
223
224 if(!turned_over)
225 printf(" => THE TEST DID NOT RUN. Compartments did not divide and die, so no compartment-level\n"
226 " reproduction existed to select on. Invalid experiment, not a null.\n");
227 else if(!ctrl_flat)
228 printf(" => CONFOUNDED. The control rose too, so division-plus-assortment alone produces the\n"
229 " effect and the growth->catalysis coupling is not doing the work.\n");
230 else if(small_wins==2 && large_fails==2)
231 printf(" => THE CORRECTOR EMERGED, THRESHOLD AND ALL. Compartment-level differential reproduction\n"
232 " arose with no group-selection step anywhere: compartments divided because they grew,\n"
233 " grew because they took up material, took up material because their contents catalyzed.\n"
234 " And it reproduces the DECREE'S SIGNATURE -- crossing at small division sizes, failing\n"
235 " at large -- which is what makes this the mechanism behind ribozyme.c's rule rather\n"
236 " than merely another way to get a rise.\n");
237 else if(small_wins>0)
238 printf(" => PARTIAL. phi rises with the coupling and the control stays flat, so growth-coupling is\n"
239 " doing real work -- but the size threshold does not cleanly match the decreed version\n"
240 " (small %d/2, large %d/2). Report the shape as-is; do not claim the corrector.\n",
241 small_wins,large_fails);
242 else
243 printf(" => HONEST NULL, with turnover confirmed. Coupling growth to catalysis does NOT generate\n"
244 " usable compartment-level selection here: the within-compartment tragedy outruns the\n"
245 " between-compartment advantage at every division size. Ganti's coupling is necessary\n"
246 " but not sufficient, and the first unit of selection may have had to be supplied by\n"
247 " the world rather than built by the chemistry.\n");
248 return 0;
249}
chemoton.md +73 -0
……@@ -0,0 +1,73 @@
1# `chemoton.c` — compartment-level selection, emergent
2
3The first time in the arc that **differential reproduction of compartments arose instead of being
4supplied.**
5
6## What changed from `byproduct.c`
7
8`byproduct.c` coupled catalysis to wall *height* and measured that isolation alone does nothing
9(supplied walls 5.65, vs 10.78 once between-site selection was added). The active ingredient was
10never the barrier — it was that compartments **divide and are selected**, and that step was
11**decreed** in both `ribozyme.c` and `byproduct.c` ("sample a parent site ∝ mean φ").
12
13Same idea, moved to the other half: a compartment divides when it grows big enough, so differential
14reproduction reduces to differential **growth** — and if wall material comes from the chemistry
15inside, growth is automatically a function of catalytic content.
16
17> catalysis → material uptake → growth → division
18
19Plus the ingredient `byproduct.c` lacked: **material is conserved** (pool + stores + units×cost =
206000, verified exactly in every cell) and rationed when demand exceeds supply. Compartments compete.
21
22**No group-selection step exists in the code.** Compartments divide because they grew, grew because
23they took up material, took up material because their contents catalyzed. The within-compartment
24tragedy is untouched — parasites still replicate 3× faster and still take the growth slots.
25
26## Results (6 seeds, start φ = 5.99 = neutral)
27
28| div size | **COUPLED** | CONTROL (uptake blind to φ) | comps | mean size | div/gen | death/gen | `ribozyme.c` (decreed) |
29|---|---|---|---|---|---|---|---|
30| 4 | **11.68** | 6.36 | 2317 | 1.87 | 100.7 | 95.0 | 11.64 |
31| 8 | **11.45** | 6.00 | 1397 | 3.41 | 23.4 | 20.8 | 11.34 |
32| 16 | 7.65 | 5.76 | 798 | 6.34 | 5.7 | 5.1 | 5.90 |
33| 32 | 5.86 | 5.62 | 426 | 12.25 | 1.2 | 1.8 | 5.73 |
34
35- **C5 passed** — real turnover (100.7 divisions/gen at size 4 down to 1.2 at 32), so
36 compartment-level reproduction genuinely existed to select on.
37- **C4 passed** — the control, where uptake is constant per unit and blind to φ, stays at or below
38 neutral in every row. Division-plus-assortment alone does nothing; the **growth→catalysis coupling
39 is doing the work**.
40- **C2 passed** — φ rises to 11.68 from a neutral start of 5.99.
41
42## C3, the key test: PARTIAL by the pre-registered bar
43
44The threshold reappeared, and at the working sizes the agreement with the decreed version is close
45to exact — **11.68 vs 11.64** at size 4, **11.45 vs 11.34** at size 8, **5.86 vs 5.73** at 32. Three
46of four points land on `ribozyme.c`.
47
48The fourth does not: at division size 16 this gives **7.65** where the decreed version gave 5.90. So
49the transition is a **soft shoulder rather than a cliff**, and by the pre-registered criterion
50(large sizes must fail) C3 scores small 2/2, large 1/2 — **partial, not a clean match.**
51
52A likely reason, stated as a caveat rather than a rescue: the two size axes are not the same
53quantity. Here compartments divide at ≥ div size but die back continuously, so the **realized mean
54size is roughly half the threshold** (1.87, 3.41, 6.34, 12.25). "Division size 16" is a population
55of ~6-unit compartments, which on ribozyme's axis sits in the working range. That plausibly accounts
56for the shift, but it is a post-hoc reading and the honest verdict stays PARTIAL.
57
58## What this establishes, and what it does not
59
60**Establishes:** compartment-level differential reproduction does not have to be supplied. Coupling
61growth to the internal chemistry's own output generates it, with no fitness-proportional sampling
62anywhere, and it reproduces the decreed version's *signature* — crossing at small division sizes,
63failing at large — not merely some rise. That answers the question `byproduct.c` opened: **where
64differential reproduction of compartments comes from.**
65
66**Does not establish:** rung 1→2. This is still the fidelity/replicase half of `ribozyme.c` — φ is a
67supplied fold→catalysis map, and what rises is catalytic quality, not open-ended heredity. The
68compartment now builds and reproduces itself; the *template* still does not. And the threshold match
69is partial.
70
71This is Gánti's chemoton coupling, which is the field's standing answer — the contribution here is
72testing it against our own measured claim that isolation is insufficient, and finding that the
73coupling supplies exactly the half isolation could not.