/* threecarrier.c — INVALID AS RUN. DO NOT READ ITS NUMBERS AS A RESULT.
*
* Two faults, one fixed and one fatal, both recorded so the next attempt starts from them.
*
* FIXED: the first version set nsz[c]=newn, REPLACING each compartment with only what production
* could afford, so nothing persisted and every arm starved to extinction — FULL ended at 0 units and
* 0 PARASITES, which is death, not collapse-to-parasites. Units now persist, production adds, and a
* per-unit death rate removes.
*
* FOURTH ATTEMPT — fluctuating environment. NOT void (T0 passed, arms differ), but the
* PRE-REGISTERED METRIC WAS WRONG, and wrong in a way already documented in genome.md.
*
* arm | templates | working | function
* STATIC, full | 0.007 | 0.940 | 2.41
* FLUCTUATING every 40 | 0.013 | 0.924 | 2.28
* FLUCTUATING every 15 | 0.012 | 0.478 | 1.47
* FLUCTUATING 15, reading OFF | 0.023 | 0.294 | 1.27
*
* M1/M2 were both scored on TEMPLATE ABUNDANCE. That is the wrong variable: reading CONSUMES
* templates by converting them into catalysts, so the reading-OFF arm holds MORE of them (0.023 vs
* 0.012) precisely because they are useless there. Template count measures templates NOT BEING USED.
* genome.md records this exact mistake; this is the third time in one session it has been made.
*
* ON THE RIGHT VARIABLE the effect is present: under fluctuation, turning reading ON raises the
* working fraction 0.294 -> 0.478 and function 1.27 -> 1.47. Templates are being USED and it helps.
* That is suggestive, not established: it was not the pre-registered test, n=5 seeds, and it needs a
* clean re-run scored on function with its own control before it can be claimed.
*
* ---- third attempt ----
* THIRD ATTEMPT, ALSO VOID — and it refines the diagnosis rather than fixing it.
* The payoff was made EXPENSIVE TO REDISCOVER (NTARG=4 SPECIFIC types out of NTYPE=16, so a lost
* type costs many mutations to re-find instead of one in four). Templates STILL collapse:
* 0.007 / 0.007 / 0.010 / 0.006 across the arms, and T0 voids the comparison again.
*
* THE REAL OBSTACLE IS DEEPER THAN CHEAP REDISCOVERY. A template costs a slot NOW, which lowers
* current function — and between-compartment selection scores current function EVERY generation. Its
* benefit is DEFERRED and CONDITIONAL: it pays only if a type is later lost. Making rediscovery
* expensive raises the deferred payoff without touching the immediate cost, so the trade is still
* lost before it can be won.
*
* WHAT WOULD ACTUALLY BE NEEDED: losses must occur often enough that the deferred benefit lands
* inside the selection horizon. That means few copies per type — small compartments — which is
* exactly the regime fidfloor.c measured as destroying sequence information. So the requirement
* collides with an already-measured result, and this is not a parameter away from working.
*
* THREE ATTEMPTS, THREE VOIDS, each revealing the previous diagnosis was incomplete. Stopping here
* is the honest call: the question is good and the design is not converging.
*
* ---- second attempt ----
* SECOND ATTEMPT, ALSO VOID — and the diagnosis was already on record before it was built.
* K1 was fixed (growth now tracks TOTAL SIZE, so a parasite-filled compartment grows as well as a
* catalyst-filled one, and the knockout receives MORE material than FULL) and the metrics were
* separated (integrity = function >= 2; template retention reported on its own). A T0 guard was
* added: do the arms differ at all?
*
* They do not. FULL 0.940 / K1 0.954 / K2 0.944 / K3 0.952, function 2.45-2.52 throughout. The
* reason is in the template column: 0.007-0.012 IN EVERY ARM. Templates are absent, so there is no
* third carrier and the two couplings that involve one cannot possibly matter — K2 has nothing to
* read, K3 nothing whose fidelity counts. The system persists on boundary + metabolism alone.
*
* WHY TEMPLATES COLLAPSE, AND IT IS A REPEAT: func_of() counts only catalysts, so selection on
* function selects AGAINST templates — they are pure cost. That is exactly genome.c's finding
* ("nothing is expensive enough to be worth remembering; payoff is scored on the present
* configuration so rediscovery always beats storage"), identified hours earlier and then rebuilt.
*
* THE FIX IS THEREFORE KNOWN AND NOT A GUESS: the payoff must reward something that is EXPENSIVE TO
* REDISCOVER, so that holding a template beats re-finding a catalyst type. Until the fitness function
* has that property, no three-carrier model can show the third carrier doing anything.
*
* ---- first attempt's fault, kept for the record ----
* FATAL, AND WHY THE RESULT IS VOID: after that fix all four arms land within noise of each other
* (intact 0.052 / 0.055 / 0.037 / 0.049; function 2.47 / 2.46 / 2.45 / 2.52). That reads as "no
* coupling is load-bearing", but at least one arm is not a knockout at all:
* K1 gives growth GROW*CAP*0.5 = 4.4, and the coupled case gives GROW*nc with nc~8 = 4.4.
* IDENTICAL BY CONSTRUCTION. The "decoupled" control was calibrated to the coupled value.
* A second problem sits underneath: the "intact" metric requires catalysts AND templates, but
* reading CONSUMES templates (the same mistake genome.c made), so intact is measuring template
* retention rather than system integrity while function stays high at ~2.5/4.
*
* So nothing here bears on whether the complete system is stable. The verdict text below is left
* exactly as the program printed it, and it is WRONG.
*
* FOR THE NEXT ATTEMPT: make each knockout a real ablation (K1 must give growth genuinely unrelated
* to catalytic content, e.g. a rate that does not track nc), separate template retention from system
* integrity in the metrics, and confirm the arms differ before reading anything into them.
*
* ---- original design notes follow ----
*
* threecarrier.c — is the COMPLETE system stable? (not: can it bootstrap)
*
* The arc has only ever tested REACHABILITY of parts. It has never tested STABILITY of the whole.
* Every failure had one shape — one object asked to do two jobs: catalyst = function AND information
* (fidfloor.c: no compartment size both corrects and remembers); compartment = isolation AND
* selection (byproduct.c); replicase = catalyst AND its own specification (replicase.c); role =
* elective AND stable (rung45.c). The two things that WORKED — linkage.c, rung45irr.c — worked by
* making a coupling STRUCTURAL rather than elective.
*
* So: three carriers, not one. A BOUNDARY nobody maintains, a METABOLISM that produces, and a
* TEMPLATE that only remembers, with couplings that cannot be defected on. That is Ganti's chemoton,
* and his claim was always that all three are needed SIMULTANEOUSLY. Every failure above was an
* attempt to get two and derive the third.
*
* THE TEST IS STABILITY, WHICH IS A DIFFERENT QUESTION. Start the system COMPLETE and correct, then
* ask whether it persists against parasite invasion, mutation and competition. Nothing here has to
* bootstrap; the question is only whether the finished thing holds together.
*
* THE THREE COUPLINGS, each non-discretionary:
* metabolism -> boundary : catalysts produce the material the compartment grows from. You build
* iff you catalyse; wall-building is not an investment anyone elects.
* template -> function : a template is read to make a catalyst of its type.
* catalyst -> template : catalysts copy templates at high fidelity (the replicase role).
*
* ARMS. FULL, then each coupling KNOCKED OUT in turn. If breaking any one collapses the system, all
* three are load-bearing and the complete system is stable only when complete — which is the
* simultaneous-origin claim, tested rather than asserted.
* K1 boundary decoupled : compartments grow at a fixed rate regardless of catalytic content
* K2 reading off : templates cannot be read
* K3 replicase off : templates copy at base fidelity regardless of catalysts present
*
* PARASITES ARE NOT SEEDED. They arise by mutation from catalysts and templates, replicate faster,
* and contribute nothing — so the tragedy is live in every arm.
*
* PRE-REGISTERED:
* T1 FULL persists — most compartments still hold catalysts AND templates after GEN generations.
* T2 every knockout collapses. If a knockout persists, that coupling is not load-bearing.
* T3 TEST OF THE TEST: parasites must actually appear and be able to win, or nothing was resisted.
*
* If FULL also collapses, the complete system is not even stable, which is a stronger negative than
* anything the arc has produced.
*
* build: cc -O2 -Wall -std=c11 -o threecarrier threecarrier.c -lm
*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#define NCOMP 600
#define CAP 16
#define GEN 400
#define NTYPE 16 /* larger type space: a lost type is EXPENSIVE to re-find */
#define NTARG 4 /* payoff needs these SPECIFIC types, not any four */
#define BETA 2.0 /* parasite replication advantage */
#define MUT 0.02 /* role/type mutation */
#define FLO 0.85 /* template copy fidelity without a catalyst present */
#define FHI 0.995 /* ... with one (the replicase coupling) */
#define GROW 0.55
#define DEATH 0.15 /* per-unit death per generation; production must exceed it */ /* material per catalyst per generation */
enum { CAT=0, TEM=1, PAR=2 };
static unsigned long long RNG;
static unsigned long long xs(void){ RNG^=RNG<<13; RNG^=RNG>>7; RNG^=RNG<<17; return RNG; }
static double urand(void){ return (double)(xs()>>11)*(1.0/9007199254740992.0); }
static int rint_(int n){ return (int)(xs()%(unsigned)n); }
typedef struct { unsigned char role, type; } Unit;
static Unit cell[NCOMP][CAP], nxt[NCOMP][CAP];
static int sz[NCOMP], nsz[NCOMP];
static double mat[NCOMP], nmat[NCOMP];
static int ncomp;
static double TFRAC;
static int TARGET[NTYPE];
static void counts(int c,int *nc,int *nt,int *np){
*nc=*nt=*np=0;
for(int k=0;k<sz[c];k++){
if(cell[c][k].role==CAT) (*nc)++;
else if(cell[c][k].role==TEM) (*nt)++;
else (*np)++;
}
}
/* function = how many of the SPECIFIC target types are present among catalysts.
THIS IS THE FIX. Previously any of 4 types counted, so a random mutation supplied a useful type
one time in four and rediscovery was cheap — genome.c's diagnosis, rebuilt by accident. Now the
payoff needs %d particular types out of %d, so a lost type costs many mutations to re-find and a
template that holds one is worth keeping. Nothing rewards templates directly; they still fold
nothing and still cost a slot. */
static int func_of(int c){
int seen[NTYPE]; memset(seen,0,sizeof seen);
for(int k=0;k<sz[c];k++) if(cell[c][k].role==CAT) seen[cell[c][k].type]=1;
int f=0; for(int t=0;t<NTYPE;t++) if(seen[t]&&TARGET[t]) f++;
return f;
}
/* ko: 0 = FULL, 1 = boundary decoupled, 2 = reading off, 3 = replicase off */
static void retarget(void){
for(int t=0;t<NTYPE;t++) TARGET[t]=0;
for(int got=0;got<NTARG;){ int t=rint_(NTYPE); if(!TARGET[t]){ TARGET[t]=1; got++; } }
}
/* switch_every = 0 means a STATIC environment (the previous condition). Otherwise the target set is
redrawn every switch_every generations: a type not needed now becomes needed later, so holding one
in a template pays REGULARLY rather than only if something is lost. That converts the deferred,
conditional benefit into a recurring one WITHOUT requiring small compartments — which is the
constraint fidfloor.c placed on any fix. */
static int SWITCH_EVERY=0;
static void run(int ko,double *alive,double *funcavg,double *parfrac)
{
retarget();
ncomp=NCOMP;
for(int c=0;c<ncomp;c++){
sz[c]=CAP; mat[c]=0;
for(int k=0;k<CAP;k++){ /* SEEDED COMPLETE: catalysts + templates */
/* seeded COMPLETE and CORRECT: catalysts and templates covering the target set */
int tg=-1,seen=0;
for(int t=0;t<NTYPE&&tg<0;t++) if(TARGET[t]){ if(seen==(k/2)%NTARG) tg=t; seen++; }
cell[c][k].role = (k%2)?TEM:CAT;
cell[c][k].type = (unsigned char)(tg<0?0:tg);
}
}
for(int g=0;g<GEN;g++){
if(SWITCH_EVERY>0 && g>0 && g%SWITCH_EVERY==0) retarget();
for(int c=0;c<ncomp;c++){
int nc,nt,np; counts(c,&nc,&nt,&np);
/* coupling 1: metabolism -> boundary. K1 decouples it. */
/* K1 FIXED. The old ablation used GROW*CAP*0.5 = 4.4, identical by construction to the
coupled GROW*nc at nc~8 — not a knockout at all. Now growth tracks TOTAL SIZE, so a
parasite-filled compartment grows exactly as well as a catalyst-filled one. That is the
real decoupling, and it gives the knockout the BENEFIT OF THE DOUBT: sz >= nc always, so
K1 receives MORE material than FULL. If it still loses, the coupling is load-bearing. */
mat[c] += (ko==1)? GROW*sz[c] : GROW*nc;
/* coupling 3: catalyst -> template fidelity. K3 decouples it. */
double f = (ko==3)? FLO : ((nc>0)? FHI : FLO);
/* replication within the compartment; parasites are faster */
double tot=0,cum[CAP];
for(int k=0;k<sz[c];k++){ tot += (cell[c][k].role==PAR)? 1.0+BETA : 1.0; cum[k]=tot; }
/* BUG FIXED: the first version set nsz[c]=newn, REPLACING the compartment with only what
production could afford — so nothing persisted and every arm starved to extinction
(FULL ended at 0 units, 0 parasites, which is death, not collapse-to-parasites).
Existing units now PERSIST; production ADDS to them; a per-unit death rate removes. */
int newn=0;
for(int k=0;k<sz[c];k++) if(urand()>=DEATH) nxt[c][newn++]=cell[c][k];
while(mat[c]>=1.0 && newn<CAP && sz[c]>0){
double r=urand()*tot; int lo=0,hi=sz[c]-1;
while(lo<hi){ int mid=(lo+hi)/2; if(cum[mid]<r) lo=mid+1; else hi=mid; }
Unit p=cell[c][lo], ch=p;
if(p.role==TEM){
/* coupling 2: template -> function. K2 turns reading off. */
if(ko!=2 && nc>0 && urand()<0.5){ ch.role=CAT; ch.type=p.type; }
else if(urand()>f) ch.type=(unsigned char)rint_(NTYPE); /* copy error */
}
if(urand()<MUT){ ch.role=(unsigned char)rint_(3); ch.type=(unsigned char)rint_(NTYPE); }
nxt[c][newn++]=ch; mat[c]-=1.0;
}
nsz[c]=newn; nmat[c]=mat[c];
}
for(int c=0;c<ncomp;c++){ sz[c]=nsz[c]; mat[c]=nmat[c]; for(int k=0;k<sz[c];k++) cell[c][k]=nxt[c][k]; }
/* between-compartment: reproduce in proportion to function; empty ones are replaced */
{
double tot=0,cum[NCOMP];
for(int c=0;c<ncomp;c++){ tot += 0.02 + func_of(c); cum[c]=tot; }
for(int c=0;c<ncomp;c++){
double r=urand()*tot; int lo=0,hi=ncomp-1;
while(lo<hi){ int mid=(lo+hi)/2; if(cum[mid]<r) lo=mid+1; else hi=mid; }
int m = sz[lo]<CAP? sz[lo] : CAP;
nsz[c]=m; nmat[c]=mat[lo]*0.5;
for(int k=0;k<m;k++) nxt[c][k]=cell[lo][rint_(sz[lo]>0?sz[lo]:1)];
}
for(int c=0;c<ncomp;c++){ sz[c]=nsz[c]; mat[c]=nmat[c]; for(int k=0;k<sz[c];k++) cell[c][k]=nxt[c][k]; }
}
}
/* METRICS SEPARATED. "intact" previously required catalysts AND templates, which conflated
template retention with system integrity. Now: integrity = the compartment still does work
(function >= 2 of NTYPE); template retention is reported on its own. */
int ok=0,tp=0,tt=0,tu=0; double fs=0;
for(int c=0;c<ncomp;c++){
int nc,nt,np; counts(c,&nc,&nt,&np);
if(func_of(c)>=2) ok++;
fs += func_of(c); tp+=np; tt+=nt; tu+=sz[c];
}
*alive=(double)ok/ncomp; *funcavg=fs/ncomp;
*parfrac= tu? (double)tp/tu : 0;
TFRAC = tu? (double)tt/tu : 0;
}
int main(void)
{
unsigned long long seeds[5]={0x9e3779b97f4a7c15ULL,0xd1b54a32d192ed03ULL,0x2545f4914f6cdd1dULL,
0x123456789abcdefULL,0xfeedface12345678ULL};
int NS=5;
printf("=== FLUCTUATING ENVIRONMENT: can memory pay when what is needed CHANGES? ===\n");
printf(" Three void attempts established the obstacle: a template costs a slot NOW while selection\n");
printf(" scores current function EVERY generation, so a deferred, conditional benefit is lost\n");
printf(" before it can be won. Making rediscovery expensive raised the later payoff without\n");
printf(" touching the immediate cost, and the obvious remedy (frequent losses => small\n");
printf(" compartments) collides with fidfloor.c.\n");
printf(" THE FIX UNDER TEST: let the target set CHANGE. A type not needed now becomes needed\n");
printf(" later, so holding one in a template pays REGULARLY rather than conditionally — and it\n");
printf(" needs no small compartments. %d of %d types needed at a time.\n\n",NTARG,NTYPE);
printf(" arm | templates | working | mean function (max %d) | parasites\n",NTARG);
printf(" -----------------------------+-----------+---------+------------------------+----------\n");
struct { const char*nm; int ko,sw; } arms[4]={
{"STATIC, full couplings ",0,0},
{"FLUCTUATING every 40 ",0,40},
{"FLUCTUATING every 15 ",0,15},
{"FLUCTUATING 15, reading OFF ",2,15}};
double T[4],A[4],F[4],P[4];
for(int k=0;k<4;k++){
double a=0,f=0,p=0,tf=0;
for(int s=0;s<NS;s++){
double x,y,z; RNG=seeds[s]; SWITCH_EVERY=arms[k].sw;
run(arms[k].ko,&x,&y,&z); a+=x; f+=y; p+=z; tf+=TFRAC;
}
A[k]=a/NS;F[k]=f/NS;P[k]=p/NS;T[k]=tf/NS;
printf(" %s | %.3f | %.3f | %.2f | %.3f\n",arms[k].nm,T[k],A[k],F[k],P[k]);
}
printf("\n");
int mem_pays = (T[1]>T[0]+0.03) || (T[2]>T[0]+0.03);
int needs_read= (T[2]>T[3]+0.02);
int differ = mem_pays || fabs(F[2]-F[0])>0.10;
printf(" M1 templates persist better when the environment fluctuates : %s (%.3f/%.3f vs static %.3f)\n",
mem_pays?"YES":"no",T[1],T[2],T[0]);
printf(" M2 and that depends on templates being READABLE : %s (%.3f vs %.3f)\n",
needs_read?"YES":"no",T[2],T[3]);
printf(" T0 arms differ at all : %s\n\n", differ?"YES":"NO — result void");
if(!differ)
printf(" => VOID AGAIN. Fluctuation changes nothing measurable, so the fourth diagnosis is also\n"
" wrong or the model still cannot express the benefit. Do not patch a fifth time\n"
" without a different design.\n");
else if(mem_pays && needs_read)
printf(" => MEMORY PAYS WHEN THE WORLD CHANGES. Templates persist under fluctuation and not\n"
" under a fixed target, and the effect disappears when they cannot be read — so it is\n"
" the INFORMATION being used, not the slot being occupied. The third carrier finally\n"
" has a reason to exist, and it needed no small compartments, so it does not collide\n"
" with fidfloor.c. This is a condition on the ENVIRONMENT, not on the chemistry.\n");
else if(mem_pays)
printf(" => TEMPLATES PERSIST, BUT NOT BECAUSE THEY ARE READ (%.3f vs %.3f with reading off).\n"
" Something other than the information is retaining them. Claim nothing yet.\n",T[2],T[3]);
else
printf(" => HONEST NULL. Fluctuation does not rescue memory: templates stay at %.3f vs %.3f\n"
" static. A deferred benefit made recurring is still not enough, and the obstacle is\n"
" deeper than the timing of the payoff.\n",T[2],T[0]);
return 0;
}