/* signal.c — can an UNFORGEABLE-BUT-FAKEABLE display privatize catalysis, with no container
* and no topology?
*
* WHERE THIS COMES FROM. Three failures in the coupling thread (coupling.md, partition.md):
* reciprocity is real and selected-for but insufficient; persistence was falsified by its own
* lineage control; and a partitionable bilateral network fails because partitioning and gating turn
* out to be ANTI-correlated -- breaking the graph to exclude parasites also cuts catalysts off from
* each other.
*
* All of those tested SPATIAL boundaries: walls (containers) and edges (topology). That exhausts
* the options in chemistry. It does not exhaust them in a digital substrate, where individuality is
* routinely established by neither -- a writer set, a keypair, a signature. Membership there is not
* containment or connectivity but UNFORGEABILITY: a parasite cannot join because it cannot produce
* the credential. So the untested third kind of boundary is a VERIFICATION rule.
*
* THE RAZOR (issue #6). A rule saying "only catalysts may display" would encode the answer outright.
* So the display must be CAUSALLY THE SAME STRUCTURE that makes a unit catalytic, and faking must
* remain possible but expensive:
* catalysis phi(s) = number of complementary hairpin pairs (i, L-1-i) in the sequence
* display exhibit ONE such pair: pick a random index i, succeed iff bit_i != bit_{L-1-i}
* A unit with phi=12 succeeds on its first attempt; a unit with phi=2 needs ~6; phi=1 needs ~12.
* The expected cost of an honest display is exactly 12/phi. That IS Zahavi's handicap condition --
* signalling is cheaper for higher quality -- but it is not written down as a cost formula anywhere.
* It EMERGES from searching your own material. Crucially a parasite CAN still display; it just pays
* more attempts. Whether honest signalling is stable is left open, which is the point.
*
* WHAT IS SUPPLIED vs WHAT MUST EMERGE:
* SUPPLIED: the display primitive (show one good pair), bilateral gating (a junction opens only if
* BOTH ends displayed), and a heritable OPTION sigma = how many attempts a unit is willing to
* spend. The per-attempt cost is SWEPT, not picked.
* MUST EMERGE: honesty. Nothing prevents a low-phi unit from spending attempts until it passes.
* ABSENT: containers, space, assortment, group selection, persistent structure. The junction graph
* is rebuilt at random every generation and replication is global Wright-Fisher.
*
* Chemistry identical to ribozyme.c / coupling.c / partition.c (phi = hairpin fold, parasites 3x
* faster, F0=0.85, F1=0.999, 4800 units, 200 gens). Reference points: ribozyme well-mixed
* 5.99->5.50; coupling FRESH k=0 4.69 / k=1 5.36; partition best 5.82. Start is always ~5.99.
*
* PRE-REGISTERED (written before the first run):
* S1 no gating (all junctions open) -> phi FALLS. The public-goods baseline.
* S2 gating on honest display -> phi RISES above its random start. The crossing.
* S3 sigma free to evolve -> sigma settles somewhere non-trivial AND phi rises.
* S4 CONTROL, RANDOM display: success probability is the POPULATION MEAN rate rather than the
* unit's own phi -- same costs, same pass rate, but the display carries NO information about
* quality. If phi rises here too, the effect is the cost burden, not the signal, and S2/S3
* mean nothing.
* S5 TEST OF THE TEST: does the display actually separate quality? Report mean phi among units
* that displayed vs those that did not. If those are equal, the signal is uninformative and a
* null says nothing -- the mistake ribophi.c made with its not-actually-early window and
* partition.c made on its first run.
*
* This is hypothesis #4. If it fails too, the convergence across containers, topology and
* verification is itself the result. Do not tune to force a positive.
*
* build: cc -O2 -Wall -std=c11 -o signal signal.c -lm
*/
#include <math.h>
#include <stdio.h>
#include <stdint.h>
/* ---------- identical to ribozyme.c / coupling.c / partition.c ---------- */
#define L 24
#define PHIMAX (L/2)
#define TOTAL 4800
#define GEN 200
#define F0 0.85
#define F1 0.999
#define BETA 2.0
/* ---------- signalling ---------- */
#define LINKS 4
#define AMAX 12 /* maximum display attempts a unit may spend */
#define MUTS 0.03
#define MINW 0.05 /* replication weight floor, so cost cannot make weight negative */
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 double randn(void){ double u=urand(),v=urand(); if(u<1e-300)u=1e-300; return sqrt(-2*log(u))*cos(6.283185307*v); }
static 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; }
static 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; }
static uint32_t seq[TOTAL], nseq[TOTAL];
static double sg[TOTAL], nsg[TOTAL];
static int phic[TOTAL], disp[TOTAL], att[TOTAL];
static int lnk[TOTAL][LINKS];
static double recv[TOTAL], fid[TOTAL], cumw[TOTAL];
/* gating: 0 = none (all junctions open), 1 = honest display, 2 = RANDOM display (control) */
static double run(int gating,int evolve_s,double sfix,double catt,double sstart,
double *s_end,double *phi_start,double *disp_frac,
double *phi_disp,double *phi_nodisp,double *mean_att)
{
for(int i=0;i<TOTAL;i++){
uint32_t s=0; for(int b=0;b<L;b++) if(urand()<0.5) s|=(1u<<b);
seq[i]=s; sg[i]= evolve_s? sstart : sfix;
}
{ double sp=0; for(int i=0;i<TOTAL;i++) sp+=phi(seq[i]); *phi_start=sp/TOTAL; }
double lastD=0,lastPD=0,lastPN=0,lastA=0;
for(int g=0;g<GEN;g++){
for(int i=0;i<TOTAL;i++) phic[i]=phi(seq[i]);
/* population mean pass rate, for the RANDOM-display control */
double meanp=0; for(int i=0;i<TOTAL;i++) meanp+=(double)phic[i]/PHIMAX; meanp/=TOTAL;
/* --- display phase --- */
for(int i=0;i<TOTAL;i++){
int A=(int)(sg[i]*AMAX+0.5); if(A<0)A=0; if(A>AMAX)A=AMAX;
int used=0, ok=0;
double p = (gating==2)? meanp : (double)phic[i]/PHIMAX; /* control: quality-blind */
for(int a=0;a<A;a++){
used++;
if(urand()<p){ ok=1; break; } /* "show one of your own good pairs" */
}
disp[i]= gating? ok : 1; /* no gating -> everyone participates */
att[i]= gating? used : 0;
}
/* --- fresh random wiring, bilateral gating --- */
for(int i=0;i<TOTAL;i++) for(int l=0;l<LINKS;l++){ int j; do{ j=(int)(xs()%TOTAL); }while(j==i); lnk[i][l]=j; }
for(int i=0;i<TOTAL;i++) recv[i]=0.0;
for(int i=0;i<TOTAL;i++){
for(int l=0;l<LINKS;l++){
int j=lnk[i][l];
if(!(disp[i]&&disp[j])) continue; /* junction opens only if BOTH displayed */
recv[j]+=(double)phic[i]/PHIMAX;
recv[i]+=(double)phic[j]/PHIMAX;
}
}
for(int i=0;i<TOTAL;i++){
double R=recv[i]/(2.0*LINKS); if(R<0)R=0; if(R>1)R=1;
fid[i]=F0+(F1-F0)*R;
}
/* --- replication: parasites still 3x faster; display attempts cost --- */
double tot=0;
for(int i=0;i<TOTAL;i++){
double w=(1.0+BETA*(1.0-(double)phic[i]/PHIMAX))*(1.0-catt*att[i]);
if(w<MINW) w=MINW;
tot+=w; cumw[i]=tot;
}
for(int i=0;i<TOTAL;i++){
double r=urand()*tot; int lo=0,hi=TOTAL-1;
while(lo<hi){ int mid=(lo+hi)/2; if(cumw[mid]<r) lo=mid+1; else hi=mid; }
nseq[i]=replicate(seq[lo],fid[lo]);
double s2 = evolve_s ? sg[lo]+MUTS*randn() : sg[lo];
if(s2<0)s2=0;
if(s2>1)s2=1;
nsg[i]=s2;
}
/* S5 diagnostics, measured on the generation just scored */
{
double sd=0,sn=0,sa=0; int nd=0,nn=0;
for(int i=0;i<TOTAL;i++){ sa+=att[i]; if(disp[i]){ sd+=phic[i]; nd++; } else { sn+=phic[i]; nn++; } }
lastD=(double)nd/TOTAL; lastPD= nd? sd/nd : NAN; lastPN= nn? sn/nn : NAN; lastA=sa/TOTAL;
}
for(int i=0;i<TOTAL;i++){ seq[i]=nseq[i]; sg[i]=nsg[i]; }
}
double sp=0,ss=0;
for(int i=0;i<TOTAL;i++){ sp+=phi(seq[i]); ss+=sg[i]; }
*s_end=ss/TOTAL; *disp_frac=lastD; *phi_disp=lastPD; *phi_nodisp=lastPN; *mean_att=lastA;
return sp/TOTAL;
}
int main(void)
{
unsigned long long seeds[10];
seeds[0]=0x9e3779b97f4a7c15ULL; seeds[1]=0xd1b54a32d192ed03ULL; seeds[2]=0x2545f4914f6cdd1dULL;
for(int i=3;i<10;i++) seeds[i]=0x9e3779b97f4a7c15ULL ^ (0x100000001b3ULL*(unsigned long long)(i+1));
int NS=10;
printf("=== COSTLY SIGNAL: can an unforgeable-but-fakeable display privatize catalysis? ===\n");
printf(" Display = exhibit ONE of your own complementary pairs. phi=12 passes first try;\n");
printf(" phi=2 needs ~6 attempts. Handicap condition EMERGES from search, is not written down.\n");
printf(" Faking stays possible: a parasite may spend attempts until it passes.\n");
printf(" Junction opens only if BOTH ends displayed. No container, no space, no group selection.\n");
printf(" REFERENCE: start ~5.99. ribozyme well-mixed 5.50; coupling k=1 5.36; partition best 5.82.\n\n");
double costs[]={0.00,0.05,0.10,0.20,0.30,0.50,0.70,1.00};
int NC=(int)(sizeof(costs)/sizeof(costs[0]));
struct { const char*name; int gate; int ev; double sf; } arms[]={
{"no gating (public good)", 0, 0, 0.0},
{"HONEST display sigma=1 ", 1, 0, 1.0},
{"honest, sigma evolvable", 1, 1, 0.0},
{"RANDOM display (ctrl) ", 2, 0, 1.0},
};
int NA=4;
double best=-1e9, bestc=-1; int informative=0;
for(int c=0;c<NC;c++){
printf(" --- attempt cost = %.2f ---\n",costs[c]);
double e_ng=0,e_h=0,e_r=0;
for(int a=0;a<NA;a++){
double S=0,E=0,SS=0,DF=0,PD=0,PN=0,MA=0; double ev[16];
for(int s=0;s<NS;s++){
double se,ps,df,pd,pn,ma; RNG=seeds[s];
double e=run(arms[a].gate,arms[a].ev,arms[a].sf,costs[c],0.10,&se,&ps,&df,&pd,&pn,&ma);
ev[s]=e; E+=e; S+=ps; SS+=se; DF+=df; PD+=pd; PN+= isnan(pn)?pd:pn; MA+=ma;
}
S/=NS;E/=NS;SS/=NS;DF/=NS;PD/=NS;PN/=NS;MA/=NS;
double sd=0; for(int s=0;s<NS;s++) sd+=(ev[s]-E)*(ev[s]-E); sd=sqrt(sd/(NS-1));
printf(" %s | %.2f -> %5.2f +- %.2f | sigma %.3f | displayed %5.1f%% | phi disp/no %.2f/%.2f | att %.2f\n",
arms[a].name,S,E,sd,SS,100.0*DF,PD,PN,MA);
if(a==0) e_ng=E;
if(a==1){ e_h=E; if(PD-PN>0.3) informative=1; if(E-S>best){ best=E-S; bestc=costs[c]; } }
if(a==3) e_r=E;
}
printf(" honest gain over public good %+.2f | over RANDOM display %+.2f\n\n", e_h-e_ng, e_h-e_r);
}
printf(" S5 TEST OF THE TEST — did the display ever separate quality? %s\n",
informative?"YES (phi of displayers exceeds non-displayers)":"NO — signal uninformative");
printf(" best phi change from START, honest arm: %+.2f at cost %.2f (start ~5.99)\n\n",best,bestc);
if(!informative)
printf(" => THE TEST DID NOT RUN. The display never distinguished high-phi from low-phi units,\n"
" so nothing here speaks to whether verification can privatize catalysis. Report as an\n"
" invalid experiment, NOT as a null.\n");
else if(best>0.05)
printf(" => phi ROSE ABOVE ITS RANDOM START (%+.2f), the first mechanism in the arc to do so with\n"
" NO container. The RANDOM-display control stayed flat, so the effect is the signal and\n"
" not the cost burden. But read the size: compartments took phi to 11.6, this is a rise\n"
" off 5.99, and check whether sigma-evolvable tracks sigma=1 -- if selection drives sigma\n"
" DOWN, honest signalling works when imposed but does not self-install.\n",best);
else
printf(" => HONEST NULL, AND IT IS THE FOURTH. Containers were the only thing that ever worked;\n"
" wiring, persistence, topology and now verification all fail to privatize catalysis.\n"
" That convergence across three DIFFERENT KINDS of boundary -- spatial, topological and\n"
" credential -- is the result.\n");
/* ================= BISTABILITY TEST =================
Is sigma's collapse an INVASION (non-signallers beat signallers -> genuine second-order
public good, and the multilevel circle is real) or a COORDINATION TRAP (both sigma=1 and
sigma=0 stable, we merely started low -> the circle is an artifact of the initial condition)?
Decisive test: start the evolvable arm HIGH and see whether it stays. */
printf("\n=== BISTABILITY: does sigma STAY high if it starts high? ===\n");
printf(" invasion => sigma falls from 1.0 => second-order public good, circle REAL\n");
printf(" trap => sigma stays near 1.0 => coordination bistability, circle an ARTIFACT of start\n\n");
printf(" cost | sigma start 0.10 -> end | phi | sigma start 0.50 -> end | phi | sigma start 1.00 -> end | phi\n");
printf(" -----+-------------------------+------+-------------------------+------+------------------------+-----\n");
double starts[3]={0.10,0.50,1.00};
for(int c=0;c<NC;c++){
double se_[3],ph_[3];
for(int q=0;q<3;q++){
double SS=0,E=0;
for(int s=0;s<NS;s++){
double se,ps,df,pd,pn,ma; RNG=seeds[s];
E+=run(1,1,0.0,costs[c],starts[q],&se,&ps,&df,&pd,&pn,&ma);
SS+=se;
}
se_[q]=SS/NS; ph_[q]=E/NS;
}
printf(" %.2f | %.3f -> %.3f | %.2f | %.3f -> %.3f | %.2f | %.3f -> %.3f | %.2f\n",
costs[c],starts[0],se_[0],ph_[0],starts[1],se_[1],ph_[1],starts[2],se_[2],ph_[2]);
}
printf("\n Read the sigma=1.00 column: if it decays toward the 0.10 column, non-signallers INVADE\n");
printf(" and the circle is real. If it holds high with correspondingly high phi, the system is\n");
printf(" BISTABLE and the collapse we reported was an artifact of starting at 0.10.\n");
return 0;
}