OverviewFilesCommitsIssuesDocsReleasesPeers

hypercore-kb / basal-agent / dup.c

247 lines · c ref:
/* dup.c — does ADDING A DIMENSION cross the valley that tunnelling through it cannot?
 *
 * WHY THIS EXPERIMENT. stress.c (run 2, NULL) tested a local mechanism that crosses a
 * SPATIAL valley, in a PARAMETER-SPACE valley. It failed for the reason pre-registered.
 * Reviewing why the law "nothing local crosses" held in these models at all exposed that
 * it contradicts the repo's own results: rung34.c has compartmentalization evolving
 * UN-FORCED (0.1->0.94, frozen control collapsed) and evoworld.c crosses this very valley
 * with population+selection. PAPER.md §2 says "two things cross it"; §4 then says "cannot
 * be crossed" and "always a SUPPLIED boundary". Both cannot hold.
 *
 * The models could only ever buy TWO of the four resources reality spends on coordination:
 *   directed read (global statistics) ...... mom.c    -- had it
 *   population + selection ................. evoworld -- had it
 *   dimension-addition (duplication/HGT) ... NOTHING IN THE REPO HAS THIS
 *   a neighbour's willingness to move ...... stress.c -- tested, null
 * Every tensor here has been 27 fixed parameters forever. With no way to ADD a degree of
 * freedom, a search can only tunnel THROUGH a barrier, never walk AROUND it. That is the
 * standard way evolution crosses valleys (Ohno duplication-divergence) and it is the one
 * mechanism the substrate structurally forbade. The wall may be a property of the
 * substrate, not of coordination.
 *
 * THE TEST. Both arms end at N=3 hidden states -- IDENTICAL final capacity, so this is not
 * a parameter-count comparison:
 *   F  fixed : start at N=3 random, anneal. (= stress.c arm B, the best local arm.)
 *   D  grow  : start at N=1, duplicate-and-diverge 1->2->3, then refine.
 * ONE KNOB: whether the search GREW into dimension 3 or STARTED there.
 *
 * D IS HANDICAPPED ON PURPOSE. It spends its first 400 sweeps below full capacity, so it
 * refines at N=3 for 3600 sweeps vs F's 4000. Scoring-call budget is reported. If D wins it
 * wins DESPITE less time at full capacity -- the conservative direction.
 *
 * Duplication = literal gene duplication: state i is copied exactly to a new index, the
 * INCOMING mass to i is split 50/50 between the two, then both are perturbed to break the
 * symmetry. An exact copy followed by divergence.
 *
 * CALIBRATED, not guessed (dscale.c): |delta| per single-entry step = 2e-5, window noise
 * = 380x that. So T=2e-5 and delta MUST be taken on a shared window (common random
 * numbers). poscheck.c verified at this T a TRUE-seeded model HOLDS (+0.0005) and near-true
 * CONVERGES (+0.0025 -> +0.0001).
 *
 * PRIMARY ENDPOINT = box-count ladder, NOT log-loss (README: prediction barely
 * distinguishes finite/infinite/fractal). Reported PER SEED this time -- run 2's ladder was
 * n=1 and could not support an ordering claim.
 *   true Mess3 396/2399/8924/26394/54821 ; learn.c collapse 5/9/14/20/36 ; optimal 0.796
 *
 * PRE-REGISTERED MOST-LIKELY NULL: the N=2 optimum may sit in the SAME basin its N=3
 * duplicate lands in, so growing buys a different starting point but not a different basin.
 * If D ~= F, that is the first explanation to reach for, not a tuning problem.
 *
 * build: cc -O2 -std=c11 -o dup dup.c -lm && ./dup
 */
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define K      3
#define NMAX   3
#define M      16
#define SWEEPS 4000
#define WIN    10000
#define NSEED  5
#define TEMP   2e-5      /* calibrated; see dscale.c / poscheck.c */
#define STEP   0.06
#define DUP1   200       /* N: 1 -> 2 */
#define DUP2   400       /* N: 2 -> 3 */

static const double TT[K][K][K]={
 {{0.765,0.00375,0.00375},{0.0425,0.0675,0.00375},{0.0425,0.00375,0.0675}},
 {{0.0675,0.0425,0.00375},{0.00375,0.765,0.00375},{0.00375,0.0425,0.0675}},
 {{0.0675,0.00375,0.0425},{0.00375,0.0675,0.0425},{0.00375,0.00375,0.765}}};

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 void gen(unsigned char*o,int n){int s=0;for(int i=0;i<n;i++){double u=urand(),a=0;int e=0,nj=0,d=0;
 for(int y=0;y<K&&!d;y++)for(int j=0;j<K&&!d;j++){a+=TT[y][s][j];if(u<a){e=y;nj=j;d=1;}}o[i]=(unsigned char)e;s=nj;}}

static long SCORECALLS;
/* variable-N edge-emitting HMM: T[sym][i][j] = P(emit sym, next j | i) */
static double llN(const double T[K][NMAX][NMAX],int N,const unsigned char*s,long off,int n){
 SCORECALLS++;
 double b[NMAX],L=0; for(int i=0;i<N;i++)b[i]=1.0/N;
 for(int t=0;t<n;t++){int x=s[off+t];double px=0,nb[NMAX];
  for(int j=0;j<N;j++)nb[j]=0;
  for(int i=0;i<N;i++)for(int j=0;j<N;j++){double m=b[i]*T[x][i][j];nb[j]+=m;px+=m;}
  if(px<1e-300)px=1e-300;
  L+=-log(px);
  for(int j=0;j<N;j++)b[j]=nb[j]/px;}
 return L/(double)n;}

static long distinctN(const double T[K][NMAX][NMAX],int N,const unsigned char*s,int n,int G){
 double b[NMAX]; for(int i=0;i<N;i++)b[i]=1.0/N;
 char*seen=calloc((size_t)(G+1)*(G+1),1); long d=0;
 for(int t=0;t<n;t++){int q0=(int)(b[0]*G+0.5),q1=(int)((N>1?b[1]:0.0)*G+0.5);
  if(q0<0)q0=0;
  if(q0>G)q0=G;
  if(q1<0)q1=0;
  if(q1>G)q1=G;
  size_t key=(size_t)q0*(G+1)+q1; if(!seen[key]){seen[key]=1;d++;}
  int x=s[t];double px=0,nb[NMAX];
  for(int j=0;j<N;j++)nb[j]=0;
  for(int i=0;i<N;i++)for(int j=0;j<N;j++){double m=b[i]*T[x][i][j];nb[j]+=m;px+=m;}
  if(px<1e-300)px=1e-300;
  for(int j=0;j<N;j++)b[j]=nb[j]/px;}
 free(seen);return d;}

static void rnN(double T[K][NMAX][NMAX],int N){
 for(int i=0;i<N;i++){double z=0;
  for(int s=0;s<K;s++)for(int j=0;j<N;j++){if(T[s][i][j]<1e-6)T[s][i][j]=1e-6;z+=T[s][i][j];}
  for(int s=0;s<K;s++)for(int j=0;j<N;j++)T[s][i][j]/=z;}}

/* gene duplication: exact copy of state `src` into index N, incoming mass split 50/50,
 * then both diverged. Returns new N. */
static int duplicate(double T[K][NMAX][NMAX],int N,int src){
 int nw=N;
 for(int s=0;s<K;s++)for(int j=0;j<N;j++) T[s][nw][j]=T[s][src][j];   /* outgoing: exact copy */
 for(int s=0;s<K;s++)for(int i=0;i<=nw;i++){                          /* incoming: split 50/50 */
  T[s][i][src]*=0.5; T[s][i][nw]=T[s][i][src]; }
 N=nw+1;
 for(int s=0;s<K;s++)for(int j=0;j<N;j++){                            /* diverge */
  T[s][src][j]*=exp(0.10*(2*urand()-1)); T[s][nw][j]*=exp(0.10*(2*urand()-1)); }
 rnN(T,N); return N;}

typedef struct { double gap; long lad[5]; long calls; int Nend; } Res;

/* arm: 0 = F fixed at N=3 ; 1 = D grow 1->2->3 */
static Res run_arm(int arm,const unsigned char*tr,long ntr,const unsigned char*te,int nte,
                   unsigned long long seed,double ll_opt)
{
 static double T[M][K][NMAX][NMAX],Tp[K][NMAX][NMAX];
 double ll[M]; int N[M];
 Res R; memset(&R,0,sizeof R);
 long c0=SCORECALLS;
 RNG=seed;
 for(int u=0;u<M;u++){
  N[u] = arm? 1 : NMAX;
  memset(T[u],0,sizeof T[u]);
  for(int i=0;i<N[u];i++){double z=0;
   for(int s=0;s<K;s++)for(int j=0;j<N[u];j++){T[u][s][i][j]=0.1+urand();z+=T[u][s][i][j];}
   for(int s=0;s<K;s++)for(int j=0;j<N[u];j++)T[u][s][i][j]/=z;}
  ll[u]=1e9;}

 for(int sw=0;sw<SWEEPS;sw++){
  long off=(long)(urand()*(double)(ntr-WIN-1));
  if(arm==1){ /* scheduled duplication -- one knob, no stall-detector second knob */
   if(sw==DUP1||sw==DUP2)
    for(int u=0;u<M;u++) if(N[u]<NMAX){ int src=(int)(urand()*N[u]); if(src>=N[u])src=N[u]-1;
                                        N[u]=duplicate(T[u],N[u],src); } }
  for(int u=0;u<M;u++){
   /* COMMON RANDOM NUMBERS: current and proposal scored on the SAME window. Run 1 of
    * stress.c compared across windows where noise is 380x the signal -> coin flip. */
   ll[u]=llN((const double(*)[NMAX][NMAX])T[u],N[u],tr,off,WIN);
   memcpy(Tp,T[u],sizeof Tp);
   int si=(int)(urand()*K),ii=(int)(urand()*N[u]),jj=(int)(urand()*N[u]);
   if(si>=K)si=K-1;
   if(ii>=N[u])ii=N[u]-1;
   if(jj>=N[u])jj=N[u]-1;
   Tp[si][ii][jj]*=exp(STEP*(2.0*urand()-1.0)); rnN(Tp,N[u]);
   double p=llN((const double(*)[NMAX][NMAX])Tp,N[u],tr,off,WIN),d=p-ll[u];
   if(d<=0||urand()<exp(-d/TEMP)){ memcpy(T[u],Tp,sizeof Tp); ll[u]=p; }
  }
 }
 int ub=0; double bb=1e9;
 for(int u=0;u<M;u++){ double v=llN((const double(*)[NMAX][NMAX])T[u],N[u],te,0,nte);
  if(v<bb){bb=v;ub=u;} }
 R.gap=bb-ll_opt; R.Nend=N[ub]; R.calls=SCORECALLS-c0;
 int Gs[5]={30,100,300,1000,3000};
 for(int g=0;g<5;g++) R.lad[g]=distinctN((const double(*)[NMAX][NMAX])T[ub],N[ub],te,nte,Gs[g]);
 return R;}

int main(void){
 const long NTR=600000; const int NTE=200000;
 unsigned char*tr=malloc(NTR),*te=malloc(NTE);
 RNG=0x9e3779b97f4a7c15ULL;gen(tr,(int)NTR); RNG=0xd1b54a32d192ed03ULL;gen(te,NTE);
 double ll_opt=llN((const double(*)[NMAX][NMAX])TT,K,te,0,NTE);

 printf("=== dup.c — does DIMENSION-ADDITION cross where tunnelling cannot? ===\n");
 printf("  M=%d units, %d sweeps, window %d, %d seeds, T=%.0e\n",M,SWEEPS,WIN,NSEED,(double)TEMP);
 printf("  F fixed: N=3 throughout   D grow: N=1 ->(sw %d) 2 ->(sw %d) 3\n",DUP1,DUP2);
 printf("  optimal log-loss %.4f nats\n\n",ll_opt);

 Res F[NSEED],D[NSEED];
 for(int sd=0;sd<NSEED;sd++){
  unsigned long long seed=0x243f6a8885a308d3ULL+0x1234567ULL*(unsigned long long)sd;
  F[sd]=run_arm(0,tr,NTR,te,NTE,seed,ll_opt);
  D[sd]=run_arm(1,tr,NTR,te,NTE,seed,ll_opt);
  fprintf(stderr,"  seed %d: F %+.4f (G3k %6ld)   D %+.4f (G3k %6ld)   Nend F%d D%d\n",
          sd,F[sd].gap,F[sd].lad[4],D[sd].gap,D[sd].lad[4],F[sd].Nend,D[sd].Nend);
 }

 /* ---------- guards ---------- */
 int vd=0;
 printf("  --- guard: did the test actually run? ---\n");
 int nd=1; for(int i=0;i<NSEED;i++){ if(F[i].Nend!=3)nd=0; if(D[i].Nend!=3)nd=0; }
 printf("  [1] both arms end at N=3 (equal capacity)? %s\n",nd?"yes":"NO");
 if(!nd)vd=1;
 long cf=0,cd=0; for(int i=0;i<NSEED;i++){cf+=F[i].calls;cd+=D[i].calls;}
 printf("  [2] scoring-call budget  F %ld  D %ld  (ratio %.3f; D handicapped if <1)\n",
        cf,cd,(double)cd/(double)cf);
 if(cd>cf){ printf("      FAIL: D got MORE compute than F.\n"); vd=1; }
 { /* [3] positive control: can this harness hold an answer it is handed? */
  double Tc[K][NMAX][NMAX],Tp2[K][NMAX][NMAX]; memcpy(Tc,TT,sizeof Tc);
  RNG=0x51ee7c0deULL;
  for(int sw=0;sw<SWEEPS;sw++){ long off=(long)(urand()*(double)(NTR-WIN-1));
   double cur=llN((const double(*)[NMAX][NMAX])Tc,K,tr,off,WIN);
   memcpy(Tp2,Tc,sizeof Tp2);
   int si=(int)(urand()*K),ii=(int)(urand()*K),jj=(int)(urand()*K);
   if(si>=K)si=K-1;
   if(ii>=K)ii=K-1;
   if(jj>=K)jj=K-1;
   Tp2[si][ii][jj]*=exp(STEP*(2.0*urand()-1.0)); rnN(Tp2,K);
   double p=llN((const double(*)[NMAX][NMAX])Tp2,K,tr,off,WIN),d=p-cur;
   if(d<=0||urand()<exp(-d/TEMP)) memcpy(Tc,Tp2,sizeof Tc); }
  double held=llN((const double(*)[NMAX][NMAX])Tc,K,te,0,NTE)-ll_opt;
  printf("  [3] positive control: TRUE-seeded ends at gap %+.4f (must stay <+0.01)\n",held);
  if(!(held<0.01))vd=1; }
 if(vd){ printf("\n  *** VOID — invalid experiment, NOT a null. ***\n"); return 1; }
 printf("  guard PASSED.\n\n");

 /* ---------- PRIMARY: ladder, PER SEED (run 2's was n=1) ---------- */
 printf("  PRIMARY — distinct belief states @ G=3000, ALL %d seeds:\n",NSEED);
 printf("    %-8s","seed"); for(int i=0;i<NSEED;i++)printf(" %8d",i); printf("   mean\n");
 double mF=0,mD=0;
 printf("    %-8s","F fixed"); for(int i=0;i<NSEED;i++){printf(" %8ld",F[i].lad[4]);mF+=F[i].lad[4];} printf(" %7.0f\n",mF/NSEED);
 printf("    %-8s","D grow ");  for(int i=0;i<NSEED;i++){printf(" %8ld",D[i].lad[4]);mD+=D[i].lad[4];} printf(" %7.0f\n",mD/NSEED);
 printf("    (true Mess3 @ G=3000: 54821 ; learn.c collapse: 36)\n\n");
 printf("  full ladder, seed 0:   %8s %8s %8s %8s %8s\n","G=30","100","300","1000","3000");
 printf("    %-8s","F fixed"); for(int g=0;g<5;g++)printf(" %8ld",F[0].lad[g]); printf("\n");
 printf("    %-8s","D grow ");  for(int g=0;g<5;g++)printf(" %8ld",D[0].lad[g]); printf("\n");
 printf("    %-8s %8d %8d %8d %8d %8d\n","TRUE",396,2399,8924,26394,54821);

 /* ---------- SECONDARY ---------- */
 double muF=0,muD=0,sepm=0;
 for(int i=0;i<NSEED;i++){muF+=F[i].gap;muD+=D[i].gap;}
 muF/=NSEED;muD/=NSEED;
 double vF=0,vD=0,vs=0;
 for(int i=0;i<NSEED;i++){vF+=(F[i].gap-muF)*(F[i].gap-muF);vD+=(D[i].gap-muD)*(D[i].gap-muD);}
 for(int i=0;i<NSEED;i++) sepm+=F[i].gap-D[i].gap; sepm/=NSEED;
 for(int i=0;i<NSEED;i++){double d=(F[i].gap-D[i].gap)-sepm;vs+=d*d;}
 printf("\n  SECONDARY — log-loss gap: F %+.4f +- %.4f | D %+.4f +- %.4f\n",
        muF,sqrt(vF/(NSEED-1)),muD,sqrt(vD/(NSEED-1)));
 printf("  SEPARATION F - D = %+.4f +- %.4f  (>0 means GROWING beat starting there)\n",
        sepm,sqrt(vs/(NSEED-1)));
 printf("  NOTE: log-loss SECONDARY by pre-registration. Judge on the ladder.\n");
 free(tr);free(te);return 0;}