Reasoning layer: analogy/induction/abduction/causal/planning over §5 geometry ops

Compose the live relational-neighborhood geometry OPERATORS into five reasoning
modes as pure, read-only C (engram_reason.{h,c}); each is proven with closed-form
constructed tests before it ships, not declared.

- ANALOGY  (Procrustes R + residual translation, apply to C, rank candidates)
- INDUCTION (combine-pooled rule geometry + point-to-manifold membership)
- ABDUCTION (best-explaining structure by point-to-manifold fit)
- CAUSAL   (centroid-cosine correlation vs directed influence: temporal
            precedence + association surviving confounder control via subtract;
            emits a correlation-vs-causation flag)
- PLANNING (geo-distance edges + Dijkstra → discrete geodesic path)

A shared point-to-manifold fit primitive underlies induction membership and
abduction ranking. engram/test/run_reason_tests.sh: 33/33 checks on both PERF
and ASan/UBSan passes; macOS leaks 0/0.

ANALOGY is surfaced as an el builtin (engram_reason_analogy_json) via the same
pass-through the §5 operators use — demonstrated callable from compiled El with a
container-capped fold (no self-host fold). The other four are C-layer only: their
set/point/timestamp inputs do not map to the flat-CSV el ABI without touching
codegen (deferred). engram_reason.c must join the server link line beside
engram_geometry.c at cutover. See docs/runbooks/2026-08-13-reasoning-operators-*.
This commit is contained in:
2026-08-13 01:33:14 -05:00
parent 85eee42106
commit a3358dfc95
8 changed files with 868 additions and 0 deletions
+27
View File
@@ -7446,6 +7446,7 @@ static char* engram_first_n_chars(const char* s, size_t n) {
#include "engram_store.h"
#include "engram_vindex.h" /* M8: ANN (HNSW) index for activation seed selection */
#include "engram_geometry.h" /* M9: centered relational-neighborhood geometry (priming) */
#include "engram_reason.h" /* reasoning layer: compositions over the §5 operators */
/* M10 REIFICATION: resident loaded form of the first-class persisted neighborhood
* records (Neighborhood + GeoMeanFrame). Built once at boot from the durable store
@@ -12395,6 +12396,32 @@ el_val_t engram_geo_analogy_json(el_val_t a_seeds, el_val_t b_seeds) {
return el_wrap_str(b.buf);
}
/* engram_reason_analogy_json(a_csv, b_csv, c_csv) — REASONING: "A:B :: C:?".
* Learns the AB transform (Procrustes rotation + residual translation) and applies
* it to C, returning the predicted point + the Procrustes frame-fit residual. This
* is the analogy MODE (engram_reason.c) surfaced over the same flat-CSV seed ABI as
* the §5 operators. The remaining reasoning modes (induction/abduction/causal/
* planning) take candidate-set / point / timestamp inputs that do not map to flat
* CSV and are C-layer only for now (see the reasoning-operators runbook). */
el_val_t engram_reason_analogy_json(el_val_t a_seeds, el_val_t b_seeds, el_val_t c_seeds) {
GeoDescriptor* A = eg_geo_build_desc(EL_CSTR(a_seeds));
GeoDescriptor* B = eg_geo_build_desc(EL_CSTR(b_seeds));
GeoDescriptor* C = eg_geo_build_desc(EL_CSTR(c_seeds));
if (!A || !B || !C) { if (A) engram_geo_free(A); if (B) engram_geo_free(B); if (C) engram_geo_free(C); return eg_geo_err("geometry unavailable"); }
GeoAnalogyResult res; char t[80]; JsonBuf b; jb_init(&b);
if (engram_reason_analogy(A, B, C, NULL, 0, &res) != 0) {
engram_geo_free(A); engram_geo_free(B); engram_geo_free(C);
return eg_geo_err("dim/frame mismatch or missing centroid");
}
jb_putc(&b, '{');
snprintf(t, sizeof t, "\"dim\":%d,\"analogy_residual\":%.6g", res.dim, res.analogy_residual); jb_puts(&b, t);
jb_puts(&b, ",\"mapped_point\":"); eg_geo_emit_vec(&b, res.mapped_point, res.dim);
jb_putc(&b, '}');
engram_reason_analogy_free(&res);
engram_geo_free(A); engram_geo_free(B); engram_geo_free(C);
return el_wrap_str(b.buf);
}
el_val_t engram_neighbors_json(el_val_t node_id, el_val_t max_depth, el_val_t direction) {
/* Re-implement here directly so we serialize without going through
* the ElList path. Walks BFS to max_depth, emits {node, edge, hops}