/* competitive.c — the competitive symmetry-breaking operator.
*
* The thermostat (critical.c) failed: it regulated usage entropy, but washout is
* itself a MAX-usage-entropy state, and — fatally — it had NO cross-state coupling,
* so it could not break the symmetric (uniform) fixed point. This operator IS the
* synergetic mechanism: hidden states COMPETE, and structured modes win over the
* uniform mode past a critical coupling.
*
* inference : soft Bayesian belief filter (UNCHANGED) -> stays smooth, traces the fractal.
* learning : mean-field replicator competition. Source-state responsibility
* r_i = P(state=i explains this symbol). Credit cred_i = r_i - r_bar.
* The "- r_bar" is LATERAL INHIBITION: a state is reinforced only if it
* explains the datum MORE than the average state, suppressed if less.
* Multiplicative (replicator) update S *= exp(eta * cred_i * t_ij),
* row-renormalized. Positive-feedback amplification of any asymmetry ->
* the symmetric fixed point (all r_i = 1/K, cred = 0) is UNSTABLE.
* threshold : structureless data -> r_i ~ 1/K -> no drift -> stays symmetric (no
* false structure); structured data -> drift -> symmetry breaks. (laser)
*
* Success = breaks the state<->symbol symmetry (diagonal -> ~0.765), approaches the
* optimal log-loss (0.794), and reconstructs the fractal, where critical.c washed out.
*
* build: cc -O2 -Wall -std=c11 -o competitive competitive.c -lm
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#define K 3
#ifndef ETA
#define ETA 0.2
#endif
#ifndef KAPPA
#define KAPPA 0.0005
#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);
/* S: per-source-row-normalized weights; T derived from S with a tiny floor */
static double S[K][K][K],T[K][K][K];
RNG=0x243f6a8885a308d3ULL;
for(int i=0;i<K;i++){ double z=0;
for(int s=0;s<K;s++)for(int j=0;j<K;j++){S[s][i][j]=0.1+urand();z+=S[s][i][j];}
for(int s=0;s<K;s++)for(int j=0;j<K;j++){S[s][i][j]/=z; T[s][i][j]=S[s][i][j];} }
double ll_init=filter_ll((const double(*)[K][K])T,te,NTE);
double b[K]={1.0/3,1.0/3,1.0/3};
for(int t=0;t<NTR;t++){
int x=tr[t];
/* soft Bayesian filter (unchanged) */
double m[K][K], px=0;
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 r[K]={0,0,0}; /* source responsibility */
for(int i=0;i<K;i++){ double si=0; for(int j=0;j<K;j++) si+=m[i][j]; r[i]=si/px; }
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; }
/* mean-field replicator competition (lateral inhibition via -r_bar) */
double rbar=1.0/K;
for(int i=0;i<K;i++){
double cred=r[i]-rbar; /* >0 winner, <0 suppressed */
double tsum=0; for(int j=0;j<K;j++) tsum+=m[i][j];
if(tsum<1e-300) continue;
for(int j=0;j<K;j++){
double tij=m[i][j]/tsum; /* within-state transition posterior */
S[x][i][j] *= exp(ETA*cred*tij); /* replicator: positive, bounded */
}
/* renormalize source row i, refresh T with tiny floor */
double z=0; for(int s=0;s<K;s++)for(int jj=0;jj<K;jj++) z+=S[s][i][jj];
if(z<1e-300)z=1e-300;
for(int s=0;s<K;s++)for(int jj=0;jj<K;jj++){
S[s][i][jj]/=z; T[s][i][jj]=(S[s][i][jj]+KAPPA)/(1.0+KAPPA*K*K);
}
}
for(int j=0;j<K;j++) b[j]=nb[j];
if(t%500000==0){
double diag=(T[0][0][0]+T[1][1][1]+T[2][2][2])/3.0;
double rspread=fabs(r[0]-rbar)+fabs(r[1]-rbar)+fabs(r[2]-rbar);
fprintf(stderr," t=%7d diag(true~.765)=%.3f r-spread=%.3f b=[%.2f %.2f %.2f]\n",
t,diag,rspread,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);
printf("=== COMPETITIVE SYMMETRY-BREAKING adapter on Mess3 (eta=%.2f) ===\n",(double)ETA);
printf(" log-loss random init : %.4f nats\n",ll_init);
printf(" log-loss LEARNED (competitive): %.4f nats\n",ll_learn);
printf(" log-loss TRUE (optimal) : %.4f nats\n",ll_opt);
printf(" gap learned - optimal : %+.4f nats (learn.c collapse +0.20 ; critical.c washout +0.30)\n",ll_learn-ll_opt);
double diag=(T[0][0][0]+T[1][1][1]+T[2][2][2])/3.0;
printf(" symmetry broken? diagonal mean : %.3f (true 0.765 ; uniform 0.111)\n",diag);
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 ; washout: 1 ; collapse: 36]\n");
free(tr); free(te); return 0;
}