-
Notifications
You must be signed in to change notification settings - Fork 1
core: robustZScore + coordinationRiskScoreRobust — 18th graduation (Amara #4 robust) + 3 BACKLOG rows #333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -526,3 +526,64 @@ module Graph = | |
| let modularityShift = qAttacked - qBaseline | ||
| Some (alpha * spectralGrowth + beta * modularityShift) | ||
| | _ -> None | ||
|
|
||
| /// **Robust-z-score variant of coordinationRiskScore.** | ||
| /// | ||
| /// Upgrades the MVP composite from raw linear differences | ||
| /// (per PR #328) to robust standardized scores per Amara | ||
| /// 17th-ferry correction #4 (robust statistics for | ||
| /// adversarial data). | ||
| /// | ||
| /// Formula: | ||
| /// ``` | ||
| /// risk = alpha * Z(λ₁_attacked; baselineLambdas) | ||
| /// + beta * Z(Q_attacked; baselineQs) | ||
| /// ``` | ||
| /// where `Z(x; baseline) = (x - median(baseline)) / | ||
| /// (1.4826 * MAD(baseline))`. | ||
| /// | ||
| /// Caller provides `baselineLambdas` + `baselineQs` — | ||
| /// sequences of metric values computed across many | ||
| /// known-null baseline samples. The `double seq` type | ||
| /// is materialized once inside `robustZScore` (see | ||
| /// RobustStats), so callers may pass arrays, lists, | ||
| /// or any `seq` form without re-enumeration cost. The | ||
| /// distributions calibrate thresholds from data rather | ||
| /// than hard-coding them. | ||
| /// | ||
| /// Returns `None` when any underlying computation is | ||
| /// undefined (empty baselines, iteration failure, etc.). | ||
| /// | ||
| /// Future expansion: the full 6-term CoordinationRiskScore | ||
| /// from Amara's 17th ferry adds Sync_S + Exclusivity_S + | ||
| /// Influence_S terms. This MVP covers λ₁ + Q — the two | ||
| /// signals with shipped primitives. Additional terms land | ||
| /// as their primitives mature. | ||
| /// | ||
| /// Provenance: external AI collaborator's 17th | ||
| /// courier ferry Part 2 correction #4 (robust | ||
| /// z-scores for adversarial data) plus the corrected | ||
| /// composite-score formula. Eighteenth graduation | ||
| /// under the Otto-105 cadence. | ||
|
Comment on lines
+563
to
+567
|
||
| let coordinationRiskScoreRobust | ||
| (alpha: double) | ||
| (beta: double) | ||
| (eigenTol: double) | ||
| (eigenIter: int) | ||
| (lpIter: int) | ||
| (baselineLambdas: double seq) | ||
| (baselineQs: double seq) | ||
| (attacked: Graph<'N>) | ||
| : double option = | ||
| match largestEigenvalue eigenTol eigenIter attacked with | ||
| | None -> None | ||
| | Some lambdaAttacked -> | ||
| let partition = labelPropagation lpIter attacked | ||
| match modularityScore partition attacked with | ||
| | None -> None | ||
| | Some qAttacked -> | ||
| match RobustStats.robustZScore baselineLambdas lambdaAttacked, | ||
| RobustStats.robustZScore baselineQs qAttacked with | ||
| | Some zLambda, Some zQ -> | ||
| Some (alpha * zLambda + beta * zQ) | ||
| | _ -> None | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -100,3 +100,48 @@ module RobustStats = | |||||||||||||
| let threshold = 3.0 * max d MadFloor | ||||||||||||||
| let kept = arr |> Array.filter (fun x -> abs (x - m) <= threshold) | ||||||||||||||
| median kept | ||||||||||||||
|
|
||||||||||||||
| /// **Robust z-score.** Given a `baseline` distribution | ||||||||||||||
| /// and a `measurement`, return | ||||||||||||||
| /// `(measurement - median(baseline)) / (1.4826 * MAD(baseline))`. | ||||||||||||||
| /// The 1.4826 constant scales MAD to be consistent with | ||||||||||||||
| /// the standard deviation of a normal distribution (so | ||||||||||||||
| /// robust z-scores are directly comparable to ordinary | ||||||||||||||
| /// z-scores when the baseline actually IS normal). | ||||||||||||||
| /// | ||||||||||||||
| /// Returns `None` when the baseline is empty. When | ||||||||||||||
| /// MAD collapses to zero (every baseline value | ||||||||||||||
| /// identical), `MadFloor` is substituted so the | ||||||||||||||
| /// function returns `Some` finite value rather than | ||||||||||||||
| /// `None` or infinity — the floor reflects "scale is | ||||||||||||||
| /// below epsilon" rather than "scale is undefined." | ||||||||||||||
| /// Per Copilot review thread 59VhYb: the earlier doc | ||||||||||||||
| /// contradicted the implementation by claiming None | ||||||||||||||
| /// on MAD=0; the implementation is the contract. | ||||||||||||||
|
Comment on lines
+118
to
+120
|
||||||||||||||
| /// Per Copilot review thread 59VhYb: the earlier doc | |
| /// contradicted the implementation by claiming None | |
| /// on MAD=0; the implementation is the contract. | |
| /// Earlier documentation incorrectly claimed `None` | |
| /// on MAD=0; the implementation-defined behavior is | |
| /// the contract. |
Copilot
AI
Apr 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1 (performance/correctness-of-comment): robustZScore claims it materializes the baseline once, but it still forces multiple full enumerations/allocations because median and mad each call Seq.toArray internally (and mad calls median, which also copies). Consider adding array-specialized helpers (e.g., medianArray/madArray) and have robustZScore call those so the baseline is actually materialized once, or update the comment if that’s not the intent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: This doc claims callers can pass any
seq“without re-enumeration cost” because it’s materialized once insiderobustZScore, butrobustZScorecurrently still triggers additionalSeq.toArraycopies viamedian/mad. Either adjust this doc to match reality, or changerobustZScoreto truly operate on the single materialized array/span.