| … | … | @@ -0,0 +1,87 @@ |
| 1 | /* window.c — the evolutionary WINDOW: adaptation is maximized at an INTERIOR copy-error rate, |
| 2 | * failing toward BOTH mu=0 (no variation for selection to act on) and mu past the error threshold |
| 3 | * (catastrophe — the assembled target can't be held). This answers "faithful copying is never |
| 4 | * perfect in nature": the target was never mu=0. Life sits in the window between MEMORY and |
| 5 | * EXPLORATION, and what a templated (tunable) error rate buys is the ability to sit there. |
| 6 | * |
| 7 | * Genome-level model (the module abstraction template.c earned): M binary modules, templated copy |
| 8 | * with per-module error mu, population P, fitness = # of TARGET modules present. The population starts |
| 9 | * SPARSE and FAR from a multi-module target it must ASSEMBLE by mutation+selection. Measured as the |
| 10 | * ABSOLUTE final adaptation (mean target modules present), NOT a start->end delta -> no headroom |
| 11 | * confound; sparse start -> mu=0 genuinely cannot reach the target (no "already solved at init"). |
| 12 | * |
| 13 | * Nothing encodes WHERE the optimum is; the window emerges from the mutation-selection balance. This |
| 14 | * REPRODUCES the known error-threshold / optimal-mutation-rate result (Eigen) in our own terms -- |
| 15 | * confirmation that our model contains the window, not a novel claim. |
| 16 | * |
| 17 | * build: cc -O2 -Wall -std=c11 -o window window.c -lm |
| 18 | */ |
| 19 | #include <math.h> |
| 20 | #include <stdio.h> |
| 21 | |
| 22 | #define M 20 /* modules (genome length) */ |
| 23 | #define T 10 /* target modules that should be PRESENT (indices 0..T-1) */ |
| 24 | #define P 300 /* population */ |
| 25 | #define G 80 /* generations */ |
| 26 | #define INITP 0.20 /* sparse start: each module present w.p. 0.20 -> target far, mostly absent */ |
| 27 | #define SS 0.70 /* selection strength: fitness = exp(SS * #target-present) */ |
| 28 | |
| 29 | static unsigned long long RNG; |
| 30 | static unsigned long long xs(void){ RNG^=RNG<<13; RNG^=RNG>>7; RNG^=RNG<<17; return RNG; } |
| 31 | static double urand(void){ return (double)(xs()>>11)*(1.0/9007199254740992.0); } |
| 32 | |
| 33 | static int pop[P][M], nxt[P][M]; |
| 34 | static double fit[P], cum[P]; |
| 35 | |
| 36 | static int ntarget(const int*g){ int s=0; for(int m=0;m<T;m++) s+=g[m]; return s; } |
| 37 | static int nnon(const int*g){ int s=0; for(int m=T;m<M;m++) s+=g[m]; return s; } |
| 38 | |
| 39 | /* one run at error rate mu. returns final mean target-present; writes final mean nontarget-present. */ |
| 40 | static double run(double mu, double *mean_non){ |
| 41 | for(int p=0;p<P;p++) for(int m=0;m<M;m++) pop[p][m]=(urand()<INITP); |
| 42 | for(int g=0;g<G;g++){ |
| 43 | double tot=0; for(int p=0;p<P;p++){ fit[p]=exp(SS*ntarget(pop[p])); tot+=fit[p]; cum[p]=tot; } |
| 44 | for(int p=0;p<P;p++){ |
| 45 | double r=urand()*tot; int lo=0,hi=P-1; while(lo<hi){int mid=(lo+hi)/2; if(cum[mid]<r)lo=mid+1; else hi=mid;} int par=lo; |
| 46 | for(int m=0;m<M;m++){ int b=pop[par][m]; if(urand()<mu) b^=1; nxt[p][m]=b; } /* templated copy, per-module flip */ |
| 47 | } |
| 48 | for(int p=0;p<P;p++) for(int m=0;m<M;m++) pop[p][m]=nxt[p][m]; |
| 49 | } |
| 50 | double st=0,sn=0; for(int p=0;p<P;p++){ st+=ntarget(pop[p]); sn+=nnon(pop[p]); } |
| 51 | *mean_non = sn/P; return st/P; |
| 52 | } |
| 53 | |
| 54 | int main(void){ |
| 55 | double mus[]={0.0,0.005,0.01,0.02,0.05,0.10,0.20,0.35,0.50}; int nMU=sizeof(mus)/sizeof(double); |
| 56 | unsigned long long seeds[]={0x9e3779b97f4a7c15ULL,0xd1b54a32d192ed03ULL,0x2545f4914f6cdd1dULL,0x123456789abcdefULL, |
| 57 | 0xfeedface12345678ULL,0xa5a5a5a5a5a5a5a5ULL,0x0f0f0f0f0f0f0f0fULL,0xcafebabedeadbeefULL}; |
| 58 | int NS=sizeof(seeds)/sizeof(seeds[0]); |
| 59 | |
| 60 | printf("=== The evolutionary WINDOW: adaptation vs per-module copy error mu ===\n"); |
| 61 | printf(" M=%d modules, target=%d must be assembled, sparse start (p=%.2f), pop=%d, %d gens, %d seeds.\n",M,T,INITP,P,G,NS); |
| 62 | printf(" Fitness = exp(%.2f * #target-present). Measured: ABSOLUTE final mean target-present (max %d).\n\n",SS,T); |
| 63 | printf(" mu | target-present (of %2d) | nontarget (of %2d, unselected)\n",T,M-T); |
| 64 | printf(" ------+------------------------+------------------------------\n"); |
| 65 | |
| 66 | double best=-1; double best_mu=-1; double tvals[16]; |
| 67 | for(int i=0;i<nMU;i++){ |
| 68 | double st=0,sn=0; |
| 69 | for(int s=0;s<NS;s++){ double mn; RNG=seeds[s]; st+=run(mus[i],&mn); sn+=mn; } |
| 70 | st/=NS; sn/=NS; tvals[i]=st; |
| 71 | int bar=(int)(st*2.0+0.5); char b[64]; int k; for(k=0;k<bar&&k<40;k++) b[k]='#'; b[k]=0; |
| 72 | printf(" %.3f | %5.2f %-14s| %4.2f\n", mus[i], st, b, sn); |
| 73 | if(st>best){ best=st; best_mu=mus[i]; } |
| 74 | } |
| 75 | printf("\n"); |
| 76 | |
| 77 | int interior = (best_mu>0.0 && best_mu<mus[nMU-1]); |
| 78 | double at_zero=tvals[0], at_top=tvals[nMU-1]; |
| 79 | printf(" peak adaptation at mu* = %.3f (target-present %.2f of %d)\n", best_mu, best, T); |
| 80 | printf(" ends: mu=0 -> %.2f (no variation: frozen at best initial), mu=%.2f -> %.2f (catastrophe)\n", at_zero, mus[nMU-1], at_top); |
| 81 | printf("\n"); |
| 82 | if(interior && best > at_zero+0.5 && best > at_top+0.5) |
| 83 | printf(" => WINDOW CONFIRMED. Adaptation peaks at an INTERIOR error rate and falls off toward BOTH\n ends: mu=0 has no variation for selection to assemble the target (frozen at the best it started\n with); high mu is past the error threshold (the assembled target randomizes away, -> ~%.0f/%d like\n the unselected bits). Faithful copying was never the goal -- the goal is the window between memory\n and exploration, and a TUNABLE (templated) error rate is what makes sitting in it possible. This\n reproduces Eigen's error-threshold / optimal-mutation-rate result in our own model.\n", at_top, T); |
| 84 | else |
| 85 | printf(" => No clean interior optimum in this range (peak mu*=%.3f). Report as-is; do not tune to force it.\n",best_mu); |
| 86 | return 0; |
| 87 | } |