/* belief_field_agent.c — the SEED: a world model that lives as a belief-FIELD over coupled units,
* infers by belief-mating, and drives a DECISION toward a setpoint. The base case of the design.
*
* Architecture (each piece traces to a validated result of the arc):
* SUBSTRATE : N units on a ring, each holding a belief vector b_i in R^3 (the "cells").
* FIELD : the collective belief across units = the world model (pattern memory). Units are
* gap-junction-coupled (diffusion) -> a COHERENCE DOMAIN = the individual (the boundary).
* INFERENCE : fast loop = BELIEF-MATING (predict via the transition operator, correct via emission),
* exactly the mess3 filter (mess3.c) distributed over the field.
* DECISION : a setpoint g (goal state); the effector picks the action that its belief predicts will
* drive the world toward g. Active-inference flavored: act to reach the setpoint.
* LEARNING : (slow loop) the operators are learned by method-of-moments (mom.c, the validated
* valley-crosser). This SEED is handed the operators to isolate the NEW parts the arc
* never built -- the distributed FIELD and the closed perception->decision->action LOOP.
*
* Environment: a CONTROLLABLE 3-cause POMDP. Hidden s in {0,1,2}; action a shifts it (next=(s+a)%3
* w.p. 1-tn, else uniform) so the effect depends on WHERE you are -> you must INFER s to act well;
* emission x is noisy (correct w.p. 1-en). Goal: hold state g. A no-model agent (acts on raw x) must
* lose to a belief agent. Validation: FIELD goal-occupancy ~ OPTIMAL filter >> NO-MODEL; and the field's
* collective belief tracks the exact filter (inference is correct) while staying coherent (boundary holds).
*
* build: cc -O2 -Wall -std=c11 -o belief_field_agent belief_field_agent.c -lm
*/
#include <math.h>
#include <stdio.h>
#define K 3 /* causes / symbols / actions */
#define N 12 /* field units (the coherence domain) */
#define TICKS 300000
#define TN 0.15 /* transition noise */
#define EN 0.60 /* emission noise: raw x is right only 60% -> reactive control fails, inference wins */
#define DIFF 0.30 /* gap-junction diffusion (keeps the coherence domain together) */
#define UNITNOISE 0.01 /* per-unit corruption (the field must denoise via coupling) */
#define GOAL 0
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 int rk(void){ return (int)(xs()%K); }
/* --- environment operators (the agent's model = these; learning them = mom.c) --- */
static double Ptrans(int j,int i,int a){ return (1.0-TN)*((j==(i+a)%K)?1.0:0.0) + TN/K; } /* P(next=j|s=i,a) */
static double Pemit (int x,int s){ return (1.0-EN)*((x==s)?1.0:0.0) + EN/K; } /* P(x|s) */
static int step_env(int s,int a){ return (urand()<1.0-TN)? (s+a)%K : rk(); }
static int emit_env(int s){ return (urand()<1.0-EN)? s : rk(); }
static void norm(double*b){ double z=0; for(int j=0;j<K;j++) z+=b[j]; if(z<1e-300)z=1e-300; for(int j=0;j<K;j++) b[j]/=z; }
/* belief-mating step (predict via transition op given action a, correct via emission of x) */
static void mate(double*b,int a,int x){
double bp[K]={0,0,0};
for(int j=0;j<K;j++){ double s=0; for(int i=0;i<K;i++) s+=b[i]*Ptrans(j,i,a); bp[j]=s; } /* predict */
for(int j=0;j<K;j++) bp[j]*=Pemit(x,j); /* correct */
norm(bp); for(int j=0;j<K;j++) b[j]=bp[j];
}
/* decision: pick the action whose predicted next-belief puts most mass on the goal g */
static int decide(const double*b){
int best=0; double bv=-1;
for(int a=0;a<K;a++){ double m=0; for(int i=0;i<K;i++) m+=b[i]*Ptrans(GOAL,i,a); if(m>bv){bv=m;best=a;} }
return best;
}
enum { OPTIMAL, FIELD, NOMODEL, RANDOM };
/* run one controller for TICKS; returns goal-occupancy. For FIELD also writes belief-divergence vs the
exact filter and inter-unit coherence. */
static double run(int type,double*field_kl,double*field_coh){
RNG=0x9e3779b97f4a7c15ULL;
int s=rk(); long hits=0;
double bf[K]={1.0/K,1.0/K,1.0/K}; /* single exact belief (OPTIMAL / shadow for FIELD) */
double u[N][K]; for(int n=0;n<N;n++) for(int j=0;j<K;j++) u[n][j]=1.0/K; /* the FIELD */
double klsum=0, cohsum=0; long m=0;
for(long t=0;t<TICKS;t++){
/* collective belief of the field */
double c[K]={0,0,0}; for(int n=0;n<N;n++) for(int j=0;j<K;j++) c[j]+=u[n][j]/N;
/* DECISION */
int a;
if(type==OPTIMAL) a=decide(bf);
else if(type==FIELD) a=decide(c);
else a=rk();
/* world steps */
s=step_env(s,a); int x=emit_env(s); if(s==GOAL) hits++;
/* INFERENCE */
if(type==OPTIMAL||type==FIELD) mate(bf,a,x); /* exact filter (also shadow for FIELD) */
if(type==FIELD){
/* each unit belief-mates, is corrupted, then diffuses toward its neighbours (gap junctions) */
double nu[N][K];
for(int n=0;n<N;n++){ double b[K]; for(int j=0;j<K;j++) b[j]=u[n][j];
mate(b,a,x);
for(int j=0;j<K;j++) b[j]*= (1.0 + UNITNOISE*(urand()*2-1));
norm(b);
int L=(n-1+N)%N,R=(n+1)%N;
for(int j=0;j<K;j++) nu[n][j]=(1-DIFF)*b[j]+DIFF*0.5*(u[L][j]+u[R][j]);
norm(nu[n]); }
for(int n=0;n<N;n++) for(int j=0;j<K;j++) u[n][j]=nu[n][j];
/* validation: field collective vs exact filter (KL), and inter-unit coherence */
double cc[K]={0,0,0}; for(int n=0;n<N;n++) for(int j=0;j<K;j++) cc[j]+=u[n][j]/N;
double kl=0; for(int j=0;j<K;j++){ double p=cc[j]<1e-9?1e-9:cc[j], q=bf[j]<1e-9?1e-9:bf[j]; kl+=p*log(p/q); }
double coh=0; for(int n=0;n<N;n++){ double d=0; for(int j=0;j<K;j++) d+=fabs(u[n][j]-cc[j]); coh+=1.0-d/2; }
klsum+=kl; cohsum+=coh/N; m++;
}
}
if(type==FIELD){ *field_kl=klsum/m; *field_coh=cohsum/m; }
return (double)hits/TICKS;
}
/* NOMODEL needs to remember last obs; simplest: re-run a dedicated loop */
static double run_nomodel(void){
RNG=0x9e3779b97f4a7c15ULL; int s=rk(),lastx=0; long hits=0;
for(long t=0;t<TICKS;t++){ int a=((GOAL-lastx)%K+K)%K; s=step_env(s,a); int x=emit_env(s); if(s==GOAL)hits++; lastx=x; }
return (double)hits/TICKS;
}
int main(void){
double kl=0,coh=0,dummy;
double opt = run(OPTIMAL,&dummy,&dummy);
double fld = run(FIELD,&kl,&coh);
double nom = run_nomodel();
double rnd = run(RANDOM,&dummy,&dummy);
printf("=== Belief-field agent (SEED) on a controllable 3-cause POMDP ===\n");
printf(" N=%d field units, goal=state %d, emission noise=%.2f, transition noise=%.2f.\n",N,GOAL,EN,TN);
printf(" Decision = act toward the setpoint using the field's belief. Chance occupancy = %.3f.\n\n",1.0/K);
printf(" controller | goal-occupancy\n");
printf(" ----------------------+---------------\n");
printf(" OPTIMAL exact filter | %.3f\n", opt);
printf(" FIELD (belief-field) | %.3f\n", fld);
printf(" NO-MODEL (raw obs) | %.3f\n", nom);
printf(" RANDOM | %.3f\n", rnd);
printf("\n");
printf(" field inference fidelity: KL(field collective || exact filter) = %.4f nats (->0 = correct)\n", kl);
printf(" coherence domain: mean inter-unit agreement = %.3f (->1 = the field holds together = one individual)\n", coh);
printf("\n");
if(fld > nom+0.04 && fld > opt-0.03 && coh > 0.95 && kl < 0.06)
printf(" => SEED WORKS. The world model living as a distributed belief-FIELD infers correctly (tracks the\n exact filter) while staying coherent (one individual), and DRIVES DECISIONS that reach the setpoint\n ~as well as the optimal controller and far better than acting on raw observation. Perception->belief\n ->decision->action is closed. Fast loop = belief-mating (mess3); boundary = the coherence domain;\n goal = the setpoint. Learning the operators = mom.c plugs into the slow loop. Base case complete.\n");
else
printf(" => Not yet: FIELD did not clearly beat NO-MODEL and match OPTIMAL with faithful inference.\n Read the numbers; adjust before claiming the seed works.\n");
return 0;
}