/* engram_reason.h — the REASONING layer: compositions over the §5 geometry * OPERATORS (engram_geometry.h). Where the operators are a relational ALGEBRA over * neighborhood descriptors, these are reasoning MODES built by CHAINING that algebra: * * ANALOGY A:B :: C:? — learn the A→B transform (Procrustes), apply to C. * INDUCTION {E_i} → rule — pool example geometries; a generalizing structure * + a membership test. * ABDUCTION x → best H — the structure whose geometry best PLACES an * observation in-distribution (inverse of prediction). * CAUSAL x ? y | Z, t — separate mere overlap (correlation) from directed * influence (temporal precedence + association that * SURVIVES controlling for confounders via subtract). * PLANNING start → goal — a trajectory (sequence of neighborhoods) through the * manifold: shortest path over geo-distance edges. * * PURE + READ-ONLY (stdlib + libm only): every function consumes GeoDescriptor(s) * (+ a few scalars / timestamps) and NEVER touches the store, index, or activation. * All geometry is delegated to the engram_geo_* primitives; this file only composes. * * FRAME CONTRACT (inherited): descriptors passed together MUST share emb `dim` and * `global_mean` frame — exactly the §5 operator contract. A function returns <0 on * a dim/frame mismatch or bad argument. */ #ifndef ENGRAM_REASON_H #define ENGRAM_REASON_H #include #include "engram_geometry.h" /* ═══════════════════════════════════════════════════════════════════════════ * SHARED PRIMITIVE — point-to-manifold FIT. How well does a single point x sit * inside a neighborhood's ellipsoid? Splits the residual (x − centroid) into: * - the IN-SUBSPACE part, scaled by each axis extent → a Mahalanobis distance * (how many "radii" out along the modeled directions), and * - the ORTHOGONAL part outside the retained axes → energy the model does not * explain at all (charged at the extent floor). * This is the common engine under INDUCTION's membership test and ABDUCTION's * explanation ranking. ext_floor (>0) guards zero-extent axes / the null model. * ═══════════════════════════════════════════════════════════════════════════ */ typedef struct { double mahalanobis; /* sqrt( Σ_k ((a_k·(x−c)) / max(ext_k,floor))² ) */ double ortho_residual; /* ‖(x−c) projected off the retained axes‖ (raw L2) */ double distance; /* sqrt( maha² + (ortho_residual/floor)² ) — full fit */ double score; /* 1 / (1 + distance²) ∈ (0,1] (1 = dead-center) */ } GeoFit; int engram_reason_point_fit(const GeoDescriptor* g, const float* x, double ext_floor, GeoFit* out); /* ═══════════════════════════════════════════════════════════════════════════ * ANALOGY — "A:B :: C:?". Learn the transform that carries A to B (orthogonal * Procrustes rotation R between their principal frames + the residual translation), * apply it to C, and return the mapped point + the nearest candidate neighborhood. * Composes: engram_geo_analogy (R) + engram_geo_analogy_apply + engram_geo_distance. * ═══════════════════════════════════════════════════════════════════════════ */ typedef struct { int dim; float* mapped_point; /* predicted D location = R·c_C + (c_B − R·c_A) (owned)*/ double analogy_residual;/* Procrustes ‖A−B R‖_F — frame-alignment quality */ int best; /* index of nearest candidate to mapped_point, or −1 */ double best_distance; /* centroid L2 from mapped_point to the winner */ int n_candidates; double* distances; /* centroid L2 mapped_point→candidate[i] (owned)*/ } GeoAnalogyResult; /* candidates may be NULL/0 (then best=−1 and only mapped_point is filled). */ int engram_reason_analogy(const GeoDescriptor* A, const GeoDescriptor* B, const GeoDescriptor* C, const GeoDescriptor* const* candidates, int n_candidates, GeoAnalogyResult* out); void engram_reason_analogy_free(GeoAnalogyResult* r); /* ═══════════════════════════════════════════════════════════════════════════ * INDUCTION — from a SET of example neighborhoods to the generalizing structure. * Pools the examples (law-of-total-variance via engram_geo_combine, folded left to * right) into a single "rule" descriptor whose top principal axes are the directions * CONSISTENTLY present across the examples (the shared subspace surfaces as the * dominant pooled axes; idiosyncratic per-example directions fall to the tail). * The rule carries a membership test (point-to-manifold fit against the pool). * ═══════════════════════════════════════════════════════════════════════════ */ typedef struct { GeoDescriptor* rule; /* induced generalizing geometry (owned; geo_free) */ double ext_floor; /* extent floor used by the membership test */ int n_examples;/* how many examples were pooled */ } GeoInduction; /* top_axes<=0 → 8. ext_floor<=0 → derived from the pooled radius. */ int engram_reason_induce(const GeoDescriptor* const* examples, int n_examples, int top_axes, double ext_floor, GeoInduction* out); /* Membership of a point in the induced rule ∈ (0,1] (the fit score). <0 on error. */ double engram_reason_membership(const GeoInduction* ind, const float* x); void engram_reason_induction_free(GeoInduction* out); /* ═══════════════════════════════════════════════════════════════════════════ * ABDUCTION — inference to the best explanation. Given an observation POINT, rank a * set of candidate structures by how well each PLACES the observation in-distribution * (min point-to-manifold distance = the structure that, if assumed, best accounts for * the observation). The inverse of prediction. * ═══════════════════════════════════════════════════════════════════════════ */ typedef struct { int best; /* index of best-explaining hypothesis, or −1 */ double best_score; int n; double* scores; /* fit score per hypothesis (higher = better) (owned)*/ double* distances; /* explanation distance per hypothesis (owned)*/ int* rank; /* hypothesis indices sorted best→worst (owned)*/ } GeoAbduction; int engram_reason_abduce(const float* obs, int dim, const GeoDescriptor* const* hypotheses, int n, double ext_floor, GeoAbduction* out); void engram_reason_abduction_free(GeoAbduction* out); /* ═══════════════════════════════════════════════════════════════════════════ * CAUSAL — correlation vs causation. Over two variables' geometries (+ candidate * confounders + temporal order), distinguish: * - mere co-occurrence / overlap (correlation), from * - directed influence: association that (a) SURVIVES controlling for confounders * (subtract each Z's subspace from both centroids, re-measure) and (b) is oriented * by temporal PRECEDENCE. * Composes: centroid cosine (correlation) + engram_geo_subtract (control) + timestamps. * ═══════════════════════════════════════════════════════════════════════════ */ typedef enum { GEO_CAUSAL_NONE = 0, /* no meaningful association */ GEO_CAUSAL_DIRECTED = 1, /* survives control + temporally ordered → cause→eff */ GEO_CAUSAL_CONFOUNDED = 2 /* correlated but association dies under control */ } GeoCausalVerdict; typedef struct { double assoc_raw; /* |cos(c_x,c_y)| — the raw correlation */ double assoc_controlled; /* |cos| of residual centroids after control */ int temporal_dir; /* +1 x→y, −1 y→x, 0 tie/unknown */ GeoCausalVerdict verdict; int confounded; /* 1 iff verdict==CONFOUNDED (the flag) */ double strength; /* directed influence estimate ∈[0,1] (0 else)*/ } GeoCausal; /* confounders may be NULL/0. t_x,t_y are comparable timestamps (any monotone unit); * pass equal values for "unknown order". drop_frac∈(0,1): a controlled association * below (1−drop_frac)·assoc_raw AND below an absolute floor ⇒ CONFOUNDED. */ int engram_reason_causal(const GeoDescriptor* x, const GeoDescriptor* y, const GeoDescriptor* const* confounders, int n_conf, int64_t t_x, int64_t t_y, double drop_frac, GeoCausal* out); /* ═══════════════════════════════════════════════════════════════════════════ * PLANNING — trajectory construction. Given a set of neighborhoods (manifold nodes), * a start and a goal, build a PATH (sequence of intermediate neighborhoods) by * shortest path over the graph whose edges connect neighborhoods within * neighbor_radius, weighted by geo-distance. Long straight jumps are not edges, so * the path follows the manifold's curvature through intermediates (a discrete geodesic). * Composes: engram_geo_distance (edge weights) + Dijkstra. * ═══════════════════════════════════════════════════════════════════════════ */ typedef struct { int* path; /* node indices start..goal (owned) */ int path_len; double total_cost; /* summed centroid-distance edge weights along path */ int reached; /* 1 if goal reachable within neighbor_radius graph */ } GeoPlan; /* neighbor_radius>0: max centroid distance for two neighborhoods to be adjacent. * Use "wasserstein"!=0 to weight edges by Wasserstein-2 instead of centroid L2. */ int engram_reason_plan(const GeoDescriptor* const* nodes, int n, int start, int goal, double neighbor_radius, int use_wasserstein, GeoPlan* out); void engram_reason_plan_free(GeoPlan* out); #endif /* ENGRAM_REASON_H */