5f3ddb8b8d
El SDK CI - dev / build-and-test (pull_request) Failing after 14m31s
LTP/LTD-style belief grounding propagated along graph edges, with union-find independence-guarded corroboration. Package: core C algorithm (gep_core.h), a self-contained deterministic proof harness with recorded output, staged runtime integration, and gated .el patches for the beat hook and HTTP route. Per the author's own LEDGER.md: built + proven on a clone, GATED pending the engine/HNSW cutover — not wired into the live beat or routes. Preserved here as a spec/reference artifact, not a request to merge into the live path.
233 lines
12 KiB
C
233 lines
12 KiB
C
/* ─────────────────────────────────────────────────────────────────────────
|
|
* gep_proof.c — PROOF LEDGER for grounded edge-propagation (task #50).
|
|
*
|
|
* Self-contained. Builds three scenarios on an in-memory GepGraph that mirrors
|
|
* the live EngramStore's flat node/edge arrays, runs the consolidation/dream
|
|
* beat (gep_beat), and prints RAW grounding before/after for each:
|
|
*
|
|
* (A) STRENGTHEN — a conjecture + N independent grounded corroborators.
|
|
* Grounding grows past threshold, GRADUATES conjecture→
|
|
* likely→grounded, then RELAXES when corroboration stops
|
|
* (nothing is settled).
|
|
* (B) DECAY — a grounded belief meets N independent CONTRADICTORY
|
|
* corroborators. Grounding decays grounded→likely→conjecture.
|
|
* (C) INDEPENDENCE GUARD — identical fan-in of N=5, weights, and standings.
|
|
* C1: 5 DISTINCT independent corroborators → grounds.
|
|
* C2: the SAME support echoed (5 mutually-linked / one node
|
|
* repeated) → collapses to 1 independent → does NOT.
|
|
*
|
|
* Build: cc -std=c11 -O2 -o gep_proof gep_proof.c -lm
|
|
* Run: ./gep_proof
|
|
* ───────────────────────────────────────────────────────────────────────── */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "gep_core.h"
|
|
|
|
#define T0 1786000000000LL /* fixed base time (ms) — deterministic */
|
|
#define BEAT_MS 60000LL /* 60s heartbeat cadence (awareness.el) */
|
|
|
|
/* Seed a node's grounding with a prior LTP event so it reads as already-grounded
|
|
* (a member of the grounded core that gravity radiates from). mag→standing:
|
|
* standing = GEP_BASE + mag (event at ~now). */
|
|
static void seed_grounded(GepNode* n, double mag, int64_t ts) {
|
|
memset(&n->gr, 0, sizeof n->gr);
|
|
gep_append(&n->gr, ts, +1, mag, 0);
|
|
}
|
|
|
|
/* Re-anchor every NON-belief node (the corroborators/refuters) as a freshly-
|
|
* grounded member of the core AT time `now`. These nodes are, by definition,
|
|
* sustained members of the grounded core — each has its OWN ongoing
|
|
* corroboration — so their standing must be read as grounded at each beat, not
|
|
* left to power-law-decay out of the core between beats. The belief-under-test
|
|
* is NEVER re-anchored: its trajectory is driven only by the propagation. */
|
|
static void anchor_core(GepGraph* g, int64_t now, double mag) {
|
|
for (int i = 0; i < g->n_nodes; i++)
|
|
if (!g->nodes[i].is_belief) seed_grounded(&g->nodes[i], mag, now);
|
|
}
|
|
|
|
static void print_node(const char* tag, GepNode* n, int64_t now) {
|
|
double s = gep_standing(&n->gr, now);
|
|
printf(" %-14s standing=%.4f band=%-10s events=%d subthresh=%d\n",
|
|
tag, s, gep_band(s), n->gr.filled, n->gr.subthreshold_hits);
|
|
}
|
|
|
|
/* Run one beat over a single belief node b and print the raw support decomposition. */
|
|
static void beat_and_report(GepGraph* g, int b, int64_t now, int beatno,
|
|
const char* note) {
|
|
anchor_core(g, now, 0.80); /* corroborators stay grounded at each beat */
|
|
double s_before = gep_standing(&g->nodes[b].gr, now);
|
|
int r_before = gep_band_rank(s_before);
|
|
double pos, neg; int np, nn, incident;
|
|
int r = gep_propagate_node(g, b, now, &pos, &neg, &np, &nn, &incident);
|
|
double s_after = gep_standing(&g->nodes[b].gr, now);
|
|
int r_after = gep_band_rank(s_after);
|
|
const char* action = (r > 0) ? "LTP (strengthen)"
|
|
: (r < 0) ? "LTD (decay)"
|
|
: (np || nn) ? "sub-threshold (no shift)"
|
|
: (incident == 0) ? "isolated (no edges)"
|
|
: "starved (no grounded neighbor)";
|
|
printf(" beat %d (t=+%llds) %s\n", beatno,
|
|
(long long)((now - T0) / 1000), note ? note : "");
|
|
printf(" incident_edges=%d pos_mass=%.4f (n_indep=%d) neg_mass=%.4f (n_indep=%d)"
|
|
" THETA=%.2f N_MIN=%d\n",
|
|
incident, pos, np, neg, nn, (double)GEP_THETA, GEP_N_MIN);
|
|
printf(" -> %-26s standing %.4f (%s) -> %.4f (%s)%s\n",
|
|
action, s_before, gep_band(s_before), s_after, gep_band(s_after),
|
|
(r_after > r_before) ? " [GRADUATED]"
|
|
: (r_after < r_before) ? " [DEMOTED]" : "");
|
|
}
|
|
|
|
/* ── Scenario A — STRENGTHEN + graduation + relaxation ───────────────────── */
|
|
static void scenario_A(void) {
|
|
printf("\n=== SCENARIO A — STRENGTHEN: convergent independent corroboration ===\n");
|
|
/* nodes[0] = the conjecture (belief). nodes[1..8] = independent corroborators,
|
|
* each already grounded, each tethered to the conjecture by a weak young
|
|
* hebbian-associate edge (weight 0.15 = ENGRAM_HEBB_LINK_W0). The corroborators
|
|
* are NOT linked to each other → fully independent. */
|
|
static GepNode nodes[9];
|
|
static GepEdge edges[8];
|
|
memset(nodes, 0, sizeof nodes);
|
|
nodes[0].id = "conjecture"; nodes[0].is_belief = 1; /* bare: standing = BASE */
|
|
for (int i = 1; i <= 8; i++) {
|
|
nodes[i].id = "corroborator";
|
|
seed_grounded(&nodes[i], 0.80, T0); /* standing ≈ 0.90 → grounded core */
|
|
}
|
|
GepGraph g = { nodes, 9, edges, 0 };
|
|
|
|
printf(" seed: conjecture has NO grounding events; corroborators pre-grounded.\n");
|
|
print_node("conjecture", &nodes[0], T0);
|
|
|
|
/* Beat 1: 3 independent corroborators have grounded up around the conjecture. */
|
|
g.n_edges = 0;
|
|
for (int i = 1; i <= 3; i++)
|
|
edges[g.n_edges++] = (GepEdge){ 0, i, 0.15, +1 };
|
|
beat_and_report(&g, 0, T0, 1, "3 independent grounded corroborators appear");
|
|
|
|
/* Beat 2: the neighborhood fills in — 5 independent corroborators now. */
|
|
g.n_edges = 0;
|
|
for (int i = 1; i <= 5; i++)
|
|
edges[g.n_edges++] = (GepEdge){ 0, i, 0.15, +1 };
|
|
beat_and_report(&g, 0, T0 + BEAT_MS, 2, "neighborhood grows to 5 corroborators");
|
|
|
|
/* Beat 3: support sustained at 5 (grounding refreshed). */
|
|
beat_and_report(&g, 0, T0 + 2 * BEAT_MS, 3, "support sustained (5)");
|
|
|
|
/* Beats 4-6: corroboration REMOVED (neighbors superseded / no longer ground).
|
|
* No new events; the collection ages → standing relaxes. Nothing is settled. */
|
|
g.n_edges = 0;
|
|
beat_and_report(&g, 0, T0 + 12 * BEAT_MS, 4, "corroboration withdrawn (+10min)");
|
|
beat_and_report(&g, 0, T0 + 60 * BEAT_MS, 5, "still withdrawn (+1h)");
|
|
beat_and_report(&g, 0, T0 + 240 * BEAT_MS, 6, "still withdrawn (+4h)");
|
|
printf(" RESULT: grounding grew automatically past threshold and graduated,\n"
|
|
" then relaxed once the independent support stopped — living,\n"
|
|
" not a latched flag.\n");
|
|
}
|
|
|
|
/* ── Scenario B — DECAY via accreting contradiction ─────────────────────── */
|
|
static void scenario_B(void) {
|
|
printf("\n=== SCENARIO B — DECAY: convergent independent CONTRADICTION ===\n");
|
|
static GepNode nodes[6];
|
|
static GepEdge edges[5];
|
|
memset(nodes, 0, sizeof nodes);
|
|
nodes[0].id = "belief"; nodes[0].is_belief = 1;
|
|
/* Seed the belief as already GROUNDED via a strong prior LTP event. */
|
|
seed_grounded(&nodes[0], 0.85, T0);
|
|
for (int i = 1; i <= 5; i++) {
|
|
nodes[i].id = "refuter";
|
|
seed_grounded(&nodes[i], 0.80, T0); /* grounded contradictors */
|
|
}
|
|
GepGraph g = { nodes, 6, edges, 0 };
|
|
|
|
printf(" seed: belief pre-grounded by a strong prior LTP event.\n");
|
|
print_node("belief", &nodes[0], T0);
|
|
|
|
/* Contradiction accretes over successive beats: 3 then 5 independent grounded
|
|
* refuters (polarity -1). Each beat past threshold appends an LTD event.
|
|
* Beat 1 runs at the seed instant so the trajectory starts from grounded. */
|
|
g.n_edges = 0;
|
|
for (int i = 1; i <= 3; i++) edges[g.n_edges++] = (GepEdge){ 0, i, 0.20, -1 };
|
|
beat_and_report(&g, 0, T0, 1, "3 independent contradictions");
|
|
|
|
g.n_edges = 0;
|
|
for (int i = 1; i <= 5; i++) edges[g.n_edges++] = (GepEdge){ 0, i, 0.20, -1 };
|
|
beat_and_report(&g, 0, T0 + BEAT_MS, 2, "contradiction broadens to 5");
|
|
beat_and_report(&g, 0, T0 + 2 * BEAT_MS, 3, "contradiction sustained (5)");
|
|
beat_and_report(&g, 0, T0 + 3 * BEAT_MS, 4, "contradiction sustained (5)");
|
|
printf(" RESULT: grounding decayed grounded->likely->conjecture under\n"
|
|
" convergent independent contradiction. The door never shut\n"
|
|
" on the belief; its history is retained (events keep growing).\n");
|
|
}
|
|
|
|
/* ── Scenario C — INDEPENDENCE GUARD ─────────────────────────────────────── */
|
|
static void scenario_C(void) {
|
|
printf("\n=== SCENARIO C — INDEPENDENCE GUARD (the load-bearing property) ===\n");
|
|
printf(" Both sub-cases: N=5 corroborators, edge weight 0.30, corroborator\n"
|
|
" standing ~0.90. ONLY difference: whether the 5 are independent.\n");
|
|
|
|
/* C1 — 5 DISTINCT INDEPENDENT corroborators (no edges among them). */
|
|
{
|
|
printf("\n -- C1: 5 DISTINCT independent corroborators --\n");
|
|
static GepNode nodes[6];
|
|
static GepEdge edges[5];
|
|
memset(nodes, 0, sizeof nodes);
|
|
nodes[0].id = "conjecture"; nodes[0].is_belief = 1;
|
|
for (int i = 1; i <= 5; i++) { nodes[i].id = "corr"; seed_grounded(&nodes[i], 0.80, T0); }
|
|
for (int i = 1; i <= 5; i++) edges[i-1] = (GepEdge){ 0, i, 0.30, +1 };
|
|
GepGraph g = { nodes, 6, edges, 5 };
|
|
print_node("conjecture", &nodes[0], T0);
|
|
beat_and_report(&g, 0, T0, 1, "5 independent corroborators (no inter-links)");
|
|
}
|
|
|
|
/* C2 — the SAME support echoed: 5 corroborators that are all mutually linked
|
|
* (a derivation clique — one source echoed through the chain). Same fan-in to
|
|
* the conjecture, same weights, same standings. Union-find collapses them to
|
|
* ONE independent component → below N_MIN → NO strengthening. */
|
|
{
|
|
printf("\n -- C2: 5 corroborators, but mutually-linked (echo of ONE source) --\n");
|
|
static GepNode nodes[6];
|
|
static GepEdge edges[9]; /* 5 to conjecture + 4 chaining corr1..corr5 */
|
|
memset(nodes, 0, sizeof nodes);
|
|
nodes[0].id = "conjecture"; nodes[0].is_belief = 1;
|
|
for (int i = 1; i <= 5; i++) { nodes[i].id = "corr"; seed_grounded(&nodes[i], 0.80, T0); }
|
|
int ne = 0;
|
|
for (int i = 1; i <= 5; i++) edges[ne++] = (GepEdge){ 0, i, 0.30, +1 };
|
|
/* chain corr1-corr2-corr3-corr4-corr5: they are the same source echoed */
|
|
for (int i = 1; i <= 4; i++) edges[ne++] = (GepEdge){ i, i+1, 0.30, +1 };
|
|
GepGraph g = { nodes, 6, edges, ne };
|
|
print_node("conjecture", &nodes[0], T0);
|
|
beat_and_report(&g, 0, T0, 1, "5 echoed (mutually-linked) corroborators");
|
|
}
|
|
|
|
/* C3 — degenerate echo: literally ONE corroborator reached by 5 parallel edges. */
|
|
{
|
|
printf("\n -- C3: ONE corroborator, reached by 5 parallel edges --\n");
|
|
static GepNode nodes[2];
|
|
static GepEdge edges[5];
|
|
memset(nodes, 0, sizeof nodes);
|
|
nodes[0].id = "conjecture"; nodes[0].is_belief = 1;
|
|
nodes[1].id = "corr"; seed_grounded(&nodes[1], 0.80, T0);
|
|
for (int i = 0; i < 5; i++) edges[i] = (GepEdge){ 0, 1, 0.30, +1 };
|
|
GepGraph g = { nodes, 2, edges, 5 };
|
|
print_node("conjecture", &nodes[0], T0);
|
|
beat_and_report(&g, 0, T0, 1, "same node, 5 parallel edges");
|
|
}
|
|
|
|
printf("\n RESULT: identical raw fan-in (5) and mass inputs; C1 grounds because\n"
|
|
" the corroboration is INDEPENDENT (5 components), C2/C3 do not\n"
|
|
" because it collapses to ONE source. Circular self-reinforcement\n"
|
|
" cannot manufacture grounding.\n");
|
|
}
|
|
|
|
int main(void) {
|
|
printf("GROUNDED EDGE-PROPAGATION — PROOF LEDGER (task #50)\n");
|
|
printf("constants: BASE=%.2f LIKELY_MIN=%.2f GROUNDED_MIN=%.2f "
|
|
"N_MIN=%d THETA=%.2f D=%.1f\n",
|
|
(double)GEP_BASE, (double)GEP_LIKELY_MIN, (double)GEP_GROUNDED_MIN,
|
|
GEP_N_MIN, (double)GEP_THETA, (double)GEP_DECAY_D);
|
|
scenario_A();
|
|
scenario_B();
|
|
scenario_C();
|
|
printf("\nDONE.\n");
|
|
return 0;
|
|
}
|