OverviewFilesCommitsIssuesDocsReleasesPeers

basal-agent / critical.c

153 lines · c ref:
/* critical.c — the criticality-regulated adapter.
 *
 * learn.c failed two ways: no regularization -> COLLAPSE (one hidden state
 * dominates usage, others die, belief geometry -> ~finite); any uniform floor ->
 * WASHOUT (conditional tensor -> uniform, belief -> 1 point). The fractal lives
 * between them, at criticality.
 *
 * The regulator (the "normalizer/metabolism" operator of the 8-processor spec)
 * separates the two knobs the failures conflated:
 *   - marginal STATE USAGE  : homeostatically pinned near MAX entropy (all states
 *     stay alive) -> defeats collapse. Done by a self-tuning inverse-temperature
 *     beta on the LEARNING responsibilities (deterministic-annealing EM, but
 *     homeostatic not scheduled): if usage entropy drops (a state is winning too
 *     much) beta is lowered (responsibilities spread, usage re-balances); if usage
 *     is already flat, beta rises to sharpen structure.
 *   - conditional TENSOR    : left sharp (only a small vanishing Dirichlet floor)
 *     -> defeats washout.
 * The true Bayesian belief (untempered) is carried for prediction + the fractal;
 * beta tempers ONLY how responsibilities are credited to the learned counts.
 *
 * Success = LEARNED filter approaches optimal log-loss (0.794) AND reconstructs
 * the fractal (distinct-belief count keeps growing with resolution), where
 * learn.c collapsed (~36) or washed out (1).
 *
 * build:  cc -O2 -Wall -std=c11 -o critical critical.c -lm
 */
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define K 3
#ifndef HFRAC
#define HFRAC 0.97      /* target usage entropy as fraction of log K */
#endif
#ifndef KAPPA
#define KAPPA 0.004     /* Dirichlet floor (anti-zero) */
#endif

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_mess3(unsigned char *out,int n){
	int s=0;
	for(int i=0;i<n;i++){ double u=urand(),acc=0; int es=0,nj=0,d=0;
		for(int sym=0;sym<K&&!d;sym++)for(int j=0;j<K&&!d;j++){acc+=TT[sym][s][j];if(u<acc){es=sym;nj=j;d=1;}}
		out[i]=(unsigned char)es; s=nj; }
}
static double filter_ll(const double T[K][K][K],const unsigned char *s,int n){
	double b[K]={1.0/3,1.0/3,1.0/3},ll=0; long k=0;
	for(int t=0;t<n;t++){ int x=s[t]; double px=0,nb[K]={0,0,0};
		for(int i=0;i<K;i++)for(int j=0;j<K;j++){double m=b[i]*T[x][i][j];nb[j]+=m;px+=m;}
		if(px<1e-300)px=1e-300; ll+=-log(px);k++; for(int j=0;j<K;j++)b[j]=nb[j]/px; }
	return ll/(double)k;
}
static long filter_distinct(const double T[K][K][K],const unsigned char *s,int n,int G){
	double b[K]={1.0/3,1.0/3,1.0/3}; 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)(b[1]*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[K]={0,0,0};
		for(int i=0;i<K;i++)for(int j=0;j<K;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<K;j++)b[j]=nb[j]/px; }
	free(seen); return d;
}

int main(void){
	const int NTR=6000000,NTE=2000000;
	unsigned char *tr=malloc(NTR),*te=malloc(NTE);
	RNG=0x9e3779b97f4a7c15ULL; gen_mess3(tr,NTR);
	RNG=0xd1b54a32d192ed03ULL; gen_mess3(te,NTE);

	static double S[K][K][K],T[K][K][K];
	RNG=0x243f6a8885a308d3ULL;
	for(int i=0;i<K;i++){ double rs=0;
		for(int s=0;s<K;s++)for(int j=0;j<K;j++){S[s][i][j]=0.1+urand();rs+=S[s][i][j];}
		for(int s=0;s<K;s++)for(int j=0;j<K;j++)T[s][i][j]=S[s][i][j]/rs; }
	double ll_init=filter_ll((const double(*)[K][K])T,te,NTE);

	/* homeostatic criticality controller state */
	double beta=0.5;                 /* inverse-temperature on learning responsibilities */
	double u[K]={1.0/3,1.0/3,1.0/3}; /* EMA of hidden-state usage */
	const double Htar=HFRAC*log((double)K), rho=0.02, muU=3e-4;
	double b[K]={1.0/3,1.0/3,1.0/3};
	double eta0=0.05,tau=50000.0;
	double beta_sum=0; long beta_n=0;

	for(int t=0;t<NTR;t++){
		int x=tr[t];
		/* true Bayesian belief update (untempered) — the real filter */
		double px=0, m[K][K];
		for(int i=0;i<K;i++)for(int j=0;j<K;j++){ m[i][j]=b[i]*T[x][i][j]; px+=m[i][j]; }
		if(px<1e-300)px=1e-300;
		double nb[K]={0,0,0}; for(int j=0;j<K;j++){for(int i=0;i<K;i++)nb[j]+=m[i][j]; nb[j]/=px;}

		/* homeostasis: track usage entropy, steer beta to keep all states alive */
		for(int i=0;i<K;i++) u[i]=(1.0-muU)*u[i]+muU*b[i];
		double Hu=0; for(int i=0;i<K;i++){ double ui=u[i]>1e-12?u[i]:1e-12; Hu+=-ui*log(ui); }
		beta += rho*(Hu-Htar);              /* usage collapsing (Hu<Htar) -> lower beta */
		if(beta<0.05)beta=0.05; if(beta>5.0)beta=5.0;
		beta_sum+=beta; beta_n++;

		/* tempered learning responsibilities r_ij ~ (b_i T)^beta (credit to counts) */
		double r[K][K], rs=0;
		for(int i=0;i<K;i++)for(int j=0;j<K;j++){ r[i][j]=pow(m[i][j],beta); rs+=r[i][j]; }
		if(rs<1e-300)rs=1e-300; for(int i=0;i<K;i++)for(int j=0;j<K;j++) r[i][j]/=rs;

		double eta=eta0*tau/(tau+t);
		for(int s=0;s<K;s++)for(int i=0;i<K;i++)for(int j=0;j<K;j++){
			S[s][i][j]*=(1.0-eta); if(s==x) S[s][i][j]+=eta*r[i][j];
		}
		/* re-estimate T with a small vanishing Dirichlet floor (anti-zero, NOT uniform-mix) */
		for(int i=0;i<K;i++){ double z=0; for(int s=0;s<K;s++)for(int j=0;j<K;j++) z+=S[s][i][j];
			double den=z+KAPPA*K*K; if(den<1e-300)den=1e-300;
			for(int s=0;s<K;s++)for(int j=0;j<K;j++) T[s][i][j]=(S[s][i][j]+KAPPA)/den; }

		for(int j=0;j<K;j++) b[j]=nb[j];

		if(t % 500000 == 0){
			/* diagonal-dominance proxy: mean of the 3 self-emit-self entries */
			double diag=(T[0][0][0]+T[1][1][1]+T[2][2][2])/3.0;
			fprintf(stderr,"  t=%7d eta=%.4f beta=%.2f Hu=%.4f diag(true~.765)=%.3f b=[%.2f %.2f %.2f]\n",
			        t, eta, beta, Hu, diag, b[0],b[1],b[2]);
		}
	}

	double ll_learn=filter_ll((const double(*)[K][K])T,te,NTE);
	double ll_opt  =filter_ll(TT,te,NTE);
	double Hu_fin=0; for(int i=0;i<K;i++){double ui=u[i]>1e-12?u[i]:1e-12;Hu_fin+=-ui*log(ui);}

	printf("=== CRITICALITY-REGULATED ADAPTER on Mess3  (Htar=%.2f*lnK, kappa=%.3f) ===\n",HFRAC,(double)KAPPA);
	printf("  log-loss  random init          : %.4f nats\n",ll_init);
	printf("  log-loss  LEARNED (regulated)  : %.4f nats\n",ll_learn);
	printf("  log-loss  TRUE (optimal)       : %.4f nats\n",ll_opt);
	printf("  gap  learned - optimal         : %+.4f nats   (learn.c collapse was +0.20)\n",ll_learn-ll_opt);
	printf("  final beta %.2f (mean %.2f) | final usage entropy %.3f / %.3f max | usage [%.3f %.3f %.3f]\n",
	       beta, beta_sum/beta_n, Hu_fin, log((double)K), u[0],u[1],u[2]);
	printf("  FRACTAL PROBE (learned tensor)  distinct belief states vs resolution:\n");
	int Gs[]={30,100,300,1000,3000}; long prev=0; int prevG=0;
	for(int gi=0;gi<5;gi++){ long d=filter_distinct((const double(*)[K][K])T,te,NTE,Gs[gi]);
		double dim=(prev&&prevG)?log((double)d/prev)/log((double)Gs[gi]/prevG):0.0;
		printf("    G=%-5d : %8ld distinct   (box-dim slope %.2f)\n",Gs[gi],d,dim); prev=d;prevG=Gs[gi]; }
	printf("    [true Mess3: 396/2399/8924/26394/54821 ; learn.c collapse: 5/9/14/20/36]\n");
	free(tr); free(te); return 0;
}