nx_analyst_causal.nx
buildroot/runtime/nx_analyst_causal.nx
about
nx_analyst_causal.nx -- F1005: the CAUSAL layer, and mostly the honesty about not having one.
WHY THIS EXISTS. F1004 gave the analyst partial correlation and wired ai_confound_scan into the driver
claim -- it controls for whichever candidate WEAKENS the relationship most. That is a defect, and this
organ is the fix. "Control for everything, especially whatever moves the number" is a known statistical
error, because a third variable's ROLE decides whether controlling for it is right, useless, or actively
harmful, and no amount of correlation arithmetic can recover that role from the numbers:
CONFOUNDER (z -> x, z -> y) : you MUST adjust. Not adjusting leaves a spurious association.
MEDIATOR (x -> m -> y) : you must NOT adjust. Adjusting removes the very effect being measured.
COLLIDER (x -> c <- y) : you must NOT adjust. Adjusting MANUFACTURES an association from nothing
-- the gate demonstrates two independent columns acquiring a strong
negative partial correlation purely by conditioning on their sum.
All three look identical to a correlation scan. The only sound source of a role is a DECLARATION from
whoever knows the domain, so that is what this organ takes -- and with no declaration it returns
ASSOCIATION_ONLY and refuses to dress an argmax as a cause.
SCOPE, STATED PLAINLY: this implements the backdoor rule for a DECLARED graph -- adjust for declared
confounders, refuse declared colliders and mediators, refuse when nothing valid exists. It does NOT do
causal discovery (learning the graph from data), instrumental variables, or sensitivity analysis to
unmeasured confounding. Those are still GAP.
license_tier: ORIGINAL No hardware writes (Rule 26).
dependencies 2 imports · 2 importers
imports: nx_syscalls.nxnx_analyst_infer.nx
imported by: nx_analyst_causal_gate.nxnx_analyze.nx
structs
| none |
consts
| 26 | const AC_ROLE_UNKNOWN: i64 = 0 |
| 27 | const AC_ROLE_CONFOUNDER: i64 = 1 |
| 28 | const AC_ROLE_MEDIATOR: i64 = 2 |
| 29 | const AC_ROLE_COLLIDER: i64 = 3 |
| 32 | const AC_ASSOCIATION_ONLY: i64 = 0 // nothing declared -> no causal claim is available, full stop |
| 33 | const AC_ADJUSTED: i64 = 1 // adjusted for a declared confounder: a backdoor path is blocked |
| 34 | const AC_REFUSED_COLLIDER: i64 = 2 // asked to adjust for a collider -> would INDUCE association |
| 35 | const AC_REFUSED_MEDIATOR: i64 = 3 // asked to adjust for a mediator -> would REMOVE the real effect |
| 36 | const AC_REFUSED_NOSET: i64 = 4 // roles declared, but no confounder available to adjust for |
functions
| 38 | func ac_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { d[o + i] = s[i]; i = i + 1 } return o + i } called by 1: ac_explain |
| 41 | func ac_is_valid_control(role: i64) -> i64 { if role == AC_ROLE_CONFOUNDER { return 1 } return 0 } |
| 44 | func ac_any_declared(roles: *i64, ncand: i64) -> i64 called by 1: ac_verdict |
| 52 | func ac_adjustment_set(roles: *i64, ncand: i64, skip: i64, out: *i64) -> i64 |
| 64 | func ac_verdict(roles: *i64, ncand: i64, expo: i64, ctrl: i64) -> i64 |
| 80 | func ac_explain(verdict: i64, out: *u8) -> i64 |