/* engram_verify.c — the VERIFIER layer. Pure compositions over engram_reason.h + * engram_geometry.h. stdlib + libm only; READ-ONLY over its inputs; touches no * store/index/activation. See engram_verify.h for the design and the frame contract. */ #include "engram_verify.h" #include #include #include /* ── small float-vector helpers (mirror engram_reason.c) ────────────────────── */ static double vdot(const float* a, const float* b, int dim) { double s = 0; for (int i = 0; i < dim; i++) s += (double)a[i] * (double)b[i]; return s; } static double l2(const float* a, const float* b, int dim) { double s = 0; for (int i = 0; i < dim; i++) { double d = (double)a[i] - (double)b[i]; s += d * d; } return sqrt(s); } /* ═══════════════════════════════════════════════════════ GROUNDING ══════════ */ int engram_verify_grounding(const float* claim, int dim, const GeoDescriptor* const* evidence, int n_evidence, double ext_floor, double ground_threshold, GeoGrounding* out) { if (!claim || dim <= 0 || !evidence || n_evidence < 1 || !out) return -1; if (!(ext_floor > 0)) ext_floor = 1.0; if (!(ground_threshold > 0 && ground_threshold < 1)) ground_threshold = 0.5; memset(out, 0, sizeof *out); out->n_evidence = n_evidence; out->best = -1; out->nearest_centroid_l2 = INFINITY; out->scores = malloc((size_t)n_evidence * sizeof(double)); if (!out->scores) return -1; double best = -1; for (int i = 0; i < n_evidence; i++) { const GeoDescriptor* e = evidence[i]; GeoFit f; if (!e || e->dim != dim || !e->centroid || engram_reason_point_fit(e, claim, ext_floor, &f) != 0) { out->scores[i] = 0.0; continue; } out->scores[i] = f.score; double cl2 = l2(claim, e->centroid, dim); if (cl2 < out->nearest_centroid_l2) out->nearest_centroid_l2 = cl2; if (out->best < 0 || f.score > best) { best = f.score; out->best = i; out->grounding = f.score; out->best_distance = f.distance; out->best_ortho = f.ortho_residual; } } if (out->best < 0) { out->grounding = 0.0; out->best_distance = INFINITY; } out->grounded = (out->grounding >= ground_threshold) ? 1 : 0; return 0; } void engram_verify_grounding_free(GeoGrounding* out) { if (!out) return; free(out->scores); out->scores = NULL; } /* ═══════════════════════════════════════════════════════ CONSISTENCY ════════ */ int engram_verify_consistency(const float* claim, int dim, const GeoDescriptor* context, const GeoDescriptor* pole_pos, const GeoDescriptor* pole_neg, const GeoDescriptor* forbidden, double ext_floor, double deadzone_frac, double forbidden_thresh, double max_distance, GeoConsistency* out) { if (!claim || dim <= 0 || !out) return -1; if (!(ext_floor > 0)) ext_floor = 1.0; if (!(deadzone_frac >= 0 && deadzone_frac < 1)) deadzone_frac = 0.10; if (!(forbidden_thresh > 0 && forbidden_thresh < 1)) forbidden_thresh = 0.5; memset(out, 0, sizeof *out); out->verdict = GEO_CONSIST_OK; out->consistency = 1.0; int do_polarity = (pole_pos && pole_neg); int do_distance = (max_distance > 0); if ((do_polarity || do_distance) && (!context || context->dim != dim || !context->centroid)) return -1; if (do_polarity && (pole_pos->dim != dim || pole_neg->dim != dim || !pole_pos->centroid || !pole_neg->centroid)) return -1; if (forbidden && (forbidden->dim != dim || !forbidden->centroid)) return -1; double pol_score = 1.0, geo_score = 1.0; /* ── (a) POLARITY / negation inversion ─────────────────────────────────── */ if (do_polarity) { /* axis p = (c_pos − c_neg); midpoint o = ½(c_pos + c_neg). */ float* p = malloc((size_t)dim * sizeof(float)); float* o = malloc((size_t)dim * sizeof(float)); if (!p || !o) { free(p); free(o); return -1; } double pn2 = 0; for (int i = 0; i < dim; i++) { double dpos = (double)pole_pos->centroid[i], dneg = (double)pole_neg->centroid[i]; p[i] = (float)(dpos - dneg); o[i] = (float)(0.5 * (dpos + dneg)); pn2 += (dpos - dneg) * (dpos - dneg); } double pn = sqrt(pn2); out->polarity_separation = 0.5 * pn; if (pn > 1e-12) { /* signed positions along the axis (projection of (x − o) onto unit p). */ float* cdo = malloc((size_t)dim * sizeof(float)); /* claim − o */ float* rdo = malloc((size_t)dim * sizeof(float)); /* context − o */ if (!cdo || !rdo) { free(p); free(o); free(cdo); free(rdo); return -1; } for (int i = 0; i < dim; i++) { cdo[i] = (float)((double)claim[i] - (double)o[i]); rdo[i] = (float)((double)context->centroid[i] - (double)o[i]); } double claim_side = vdot(cdo, p, dim) / pn; /* units: emb-space length */ double ref_side = vdot(rdo, p, dim) / pn; out->polarity_claim = claim_side; out->polarity_reference = ref_side; double dz = deadzone_frac * out->polarity_separation; /* neutral band */ if (fabs(claim_side) > dz && fabs(ref_side) > dz && (claim_side > 0) != (ref_side > 0)) { out->inverted = 1; pol_score = 0.0; /* opposite poles ⇒ zero consistency */ } else if (fabs(claim_side) <= dz || fabs(ref_side) <= dz) { pol_score = 0.5; /* neutral / undecided */ } else { pol_score = 1.0; /* same pole ⇒ consistent */ } free(cdo); free(rdo); } free(p); free(o); } /* ── (b) GEOMETRIC contradiction ───────────────────────────────────────── */ if (forbidden) { GeoFit f; if (engram_reason_point_fit(forbidden, claim, ext_floor, &f) == 0) { out->forbidden_fit = f.score; if (f.score >= forbidden_thresh) { out->geo_violation = 1; double g = 1.0 - f.score; if (g < 0) g = 0; if (g < geo_score) geo_score = g; } } } if (do_distance) { out->context_distance = l2(claim, context->centroid, dim); if (out->context_distance > max_distance) { out->geo_violation = 1; geo_score = 0.0; } } /* ── verdict + scalar (polarity is the headline; both flags stay visible) ─ */ out->consistency = (pol_score < geo_score) ? pol_score : geo_score; if (out->inverted) out->verdict = GEO_CONSIST_POLARITY; else if (out->geo_violation) out->verdict = GEO_CONSIST_GEOMETRIC; else out->verdict = GEO_CONSIST_OK; return 0; }