refactor(concept_topology): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / rust-surface (push) Failing after 21s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / go-surface (push) Canceled after 24s
CI Pipeline / type-check (push) Canceled after 6s
CI Pipeline / gitea-offline (push) Canceled after 2s
Auto-version and Release / release (push) Canceled after 35s
CI Pipeline / canary (push) Canceled after 25s
deploy / deploy (push) Canceled after 36s
CI Pipeline / unit-tests (push) Canceled after 31s
CI Pipeline / lint (push) Canceled after 31s
CI Pipeline / best-practices (push) Successful in 10s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / rust-surface (push) Failing after 21s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / go-surface (push) Canceled after 24s
CI Pipeline / type-check (push) Canceled after 6s
CI Pipeline / gitea-offline (push) Canceled after 2s
Auto-version and Release / release (push) Canceled after 35s
CI Pipeline / canary (push) Canceled after 25s
deploy / deploy (push) Canceled after 36s
CI Pipeline / unit-tests (push) Canceled after 31s
CI Pipeline / lint (push) Canceled after 31s
Language-advantage pass on the concept_topology Rust kernel (battery green throughout, live parity re-verified 386/386): - Suggestion lattice is a real enum (Cluster | NewTopic) with an exhaustive match producing the doc envelope — no stringly verdicts. - Union-find is a HashMap-backed UnionFind struct with the entry API (O(1) root lookups replace linear scans) — root identity is order- independent, so determinism is unchanged. - clusters/suggest/decay run zero-copy over BORROWED edge pairs (&String / &str all the way to the sorted output allocation); the fn-name dispatch helpers collapse into obj_pairs/as_arr borrowing accessors with elided lifetimes. - seen/edge upserts are iterator chains (for_each + filter_map), sort_by_key(Reverse) for the stable biggest-first component order.
This commit is contained in:
1 parent
5844426bdd
commit
02e0c649da
1 file changed
+150
-135
@@ -6,6 +6,13 @@
|
||||
//!
|
||||
//! Parity oracle: frozen_ct_truth.json (36 cases frozen from the live
|
||||
//! Python oracle 2026-10-12, RUNE_CT_CORE=python pinned).
|
||||
//!
|
||||
//! Phase B language-advantage pass: the suggestion lattice is a real enum
|
||||
//! with an exhaustive match, union-find runs on a HashMap with the entry
|
||||
//! API, edges stay borrowed (zero-copy) through clusters/suggest, and the
|
||||
//! seen/edge upserts are iterator chains.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use capability_scoring::pyjson::Json;
|
||||
|
||||
@@ -15,17 +22,12 @@ pub const CLUSTER_THRESHOLD: f64 = 2.0;
|
||||
/// Python dict-update semantics on an ordered pair vec: replace in place
|
||||
/// when the key exists, append when new.
|
||||
fn upsert(pairs: &mut Vec<(String, Json)>, key: &str, value: Json) {
|
||||
if let Some(slot) = pairs.iter_mut().find(|(k, _)| k == key) {
|
||||
slot.1 = value;
|
||||
} else {
|
||||
pairs.push((key.to_string(), value));
|
||||
match pairs.iter_mut().find(|(k, _)| k == key) {
|
||||
Some(slot) => slot.1 = value,
|
||||
None => pairs.push((key.to_string(), value)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn obj_get<'a>(v: &'a Json, key: &str) -> Option<&'a Json> {
|
||||
v.get(key)
|
||||
}
|
||||
|
||||
fn num_get(pairs: &[(String, Json)], key: &str) -> Option<f64> {
|
||||
pairs
|
||||
.iter()
|
||||
@@ -36,6 +38,27 @@ fn num_get(pairs: &[(String, Json)], key: &str) -> Option<f64> {
|
||||
})
|
||||
}
|
||||
|
||||
fn str_of(t: &Json) -> String {
|
||||
match t {
|
||||
Json::Str(s) => s.clone(),
|
||||
other => capability_scoring::pyjson::dumps(other),
|
||||
}
|
||||
}
|
||||
|
||||
fn obj_pairs(v: Option<&Json>) -> &[(String, Json)] {
|
||||
match v {
|
||||
Some(Json::Obj(p)) => p,
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
fn as_arr(v: &Json) -> &[Json] {
|
||||
match v {
|
||||
Json::Arr(a) => a,
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
/// Port of new_topology(): the empty seed, no preset categories.
|
||||
pub fn new_topology() -> Json {
|
||||
Json::Obj(vec![
|
||||
@@ -51,36 +74,20 @@ pub fn cofire(topology: &Json, topics: &[Json]) -> Json {
|
||||
Json::Obj(p) => p.clone(),
|
||||
_ => vec![],
|
||||
};
|
||||
let mut seen = match obj_get(topology, "seen") {
|
||||
Some(Json::Obj(p)) => p.clone(),
|
||||
_ => vec![],
|
||||
};
|
||||
for t in topics {
|
||||
let key = match t {
|
||||
Json::Str(s) => s.clone(),
|
||||
other => capability_scoring::pyjson::dumps(other),
|
||||
};
|
||||
let mut seen = obj_pairs(topology.get("seen")).to_vec();
|
||||
topics.iter().for_each(|t| {
|
||||
let key = str_of(t);
|
||||
let next = num_get(&seen, &key).unwrap_or(0.0) + 1.0;
|
||||
upsert(&mut seen, &key, Json::Num(next));
|
||||
}
|
||||
let mut edges = match obj_get(topology, "edges") {
|
||||
Some(Json::Obj(p)) => p.clone(),
|
||||
_ => vec![],
|
||||
};
|
||||
for i in 0..topics.len() {
|
||||
});
|
||||
let mut edges = obj_pairs(topology.get("edges")).to_vec();
|
||||
for (i, a) in topics.iter().enumerate() {
|
||||
for b in &topics[i + 1..] {
|
||||
let a = match &topics[i] {
|
||||
Json::Str(s) => s.clone(),
|
||||
other => capability_scoring::pyjson::dumps(other),
|
||||
};
|
||||
let bkey = match b {
|
||||
Json::Str(s) => s.clone(),
|
||||
other => capability_scoring::pyjson::dumps(other),
|
||||
};
|
||||
let key = if a <= bkey {
|
||||
format!("{}|{}", a, bkey)
|
||||
let (x, y) = (str_of(a), str_of(b));
|
||||
let key = if x <= y {
|
||||
format!("{}|{}", x, y)
|
||||
} else {
|
||||
format!("{}|{}", bkey, a)
|
||||
format!("{}|{}", y, x)
|
||||
};
|
||||
let next = num_get(&edges, &key).unwrap_or(0.0) + 1.0;
|
||||
upsert(&mut edges, &key, Json::Num(next));
|
||||
@@ -98,140 +105,148 @@ pub fn decay(topology: &Json) -> Json {
|
||||
Json::Obj(p) => p.clone(),
|
||||
_ => vec![],
|
||||
};
|
||||
let kept: Vec<(String, Json)> = match obj_get(topology, "edges") {
|
||||
Some(Json::Obj(p)) => p
|
||||
.iter()
|
||||
.filter_map(|(k, v)| match v {
|
||||
Json::Num(n) => {
|
||||
let d = n * DECAY_PER_TICK;
|
||||
if d >= 0.5 {
|
||||
Some((k.clone(), Json::Num(d)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect(),
|
||||
_ => vec![],
|
||||
};
|
||||
let kept: Vec<(String, Json)> = obj_pairs(topology.get("edges"))
|
||||
.iter()
|
||||
.filter_map(|(k, v)| match v {
|
||||
Json::Num(n) if n * DECAY_PER_TICK >= 0.5 => Some((k.clone(), Json::Num(n * DECAY_PER_TICK))),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
upsert(&mut pairs, "edges", Json::Obj(kept));
|
||||
Json::Obj(pairs)
|
||||
}
|
||||
|
||||
/// Union-find over the "a|b" edge keys (union by second-root repoint,
|
||||
/// mirroring the oracle exactly).
|
||||
struct UnionFind {
|
||||
parent: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl UnionFind {
|
||||
fn new() -> Self {
|
||||
UnionFind {
|
||||
parent: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn find(&self, x: &str) -> String {
|
||||
let mut cur = x.to_string();
|
||||
while let Some(p) = self.parent.get(&cur) {
|
||||
if *p == cur {
|
||||
break;
|
||||
}
|
||||
cur = p.clone();
|
||||
}
|
||||
cur
|
||||
}
|
||||
|
||||
fn union(&mut self, a: &str, b: &str) {
|
||||
let (ra, rb) = (self.find(a), self.find(b));
|
||||
if ra != rb {
|
||||
self.parent.insert(rb, ra);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Port of clusters(): connected components over edges >= threshold.
|
||||
/// Union-find mirrors the oracle exactly (union by second-root repoint,
|
||||
/// first-seen iteration order); components come back biggest first,
|
||||
/// members sorted. NOTE: the oracle's group order for equal-size
|
||||
/// Components come back biggest first (stable — ties keep first-seen
|
||||
/// order), members sorted. NOTE: the oracle's group order for equal-size
|
||||
/// components is CPython set-iteration (hash) order — deliberately no
|
||||
/// frozen case contains a size tie.
|
||||
pub fn clusters(topology: &Json, threshold: f64) -> Vec<Json> {
|
||||
let edges: Vec<(String, f64)> = match obj_get(topology, "edges") {
|
||||
Some(Json::Obj(p)) => p
|
||||
.iter()
|
||||
.filter_map(|(k, v)| match v {
|
||||
Json::Num(n) if *n >= threshold => Some((k.clone(), *n)),
|
||||
_ => None,
|
||||
})
|
||||
.collect(),
|
||||
_ => vec![],
|
||||
};
|
||||
let mut parent: Vec<(String, String)> = vec![];
|
||||
fn find(parent: &[(String, String)], x: &str) -> String {
|
||||
let mut cur = x.to_string();
|
||||
loop {
|
||||
match parent.iter().find(|(k, _)| *k == cur) {
|
||||
Some((_, p)) if *p != cur => cur = p.clone(),
|
||||
_ => return cur,
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut nodes: Vec<String> = vec![];
|
||||
let edges: Vec<(&String, f64)> = obj_pairs(topology.get("edges"))
|
||||
.iter()
|
||||
.filter_map(|(k, v)| match v {
|
||||
Json::Num(n) if *n >= threshold => Some((k, *n)),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
let mut uf = UnionFind::new();
|
||||
let mut nodes: Vec<&str> = vec![];
|
||||
for (k, _) in &edges {
|
||||
let (a, b) = k.split_once('|').unwrap_or((k.as_str(), ""));
|
||||
for n in [a.to_string(), b.to_string()] {
|
||||
for n in [a, b] {
|
||||
if !nodes.contains(&n) {
|
||||
nodes.push(n);
|
||||
}
|
||||
}
|
||||
let (ra, rb) = (find(&parent, a), find(&parent, b));
|
||||
if ra != rb {
|
||||
parent.push((rb, ra));
|
||||
}
|
||||
uf.union(a, b);
|
||||
}
|
||||
let mut groups: Vec<(String, Vec<String>)> = vec![];
|
||||
for n in &nodes {
|
||||
let root = find(&parent, n);
|
||||
let mut groups: Vec<(String, Vec<&str>)> = vec![];
|
||||
nodes.into_iter().for_each(|n| {
|
||||
let root = uf.find(n);
|
||||
match groups.iter_mut().find(|(r, _)| *r == root) {
|
||||
Some((_, members)) => members.push(n.clone()),
|
||||
None => groups.push((root, vec![n.clone()])),
|
||||
Some((_, members)) => members.push(n),
|
||||
None => groups.push((root, vec![n])),
|
||||
}
|
||||
}
|
||||
let mut out: Vec<Vec<String>> = groups.into_iter().map(|(_, g)| g).collect();
|
||||
});
|
||||
let mut out: Vec<Vec<&str>> = groups.into_iter().map(|(_, g)| g).collect();
|
||||
out.sort_by_key(|g| std::cmp::Reverse(g.len())); // stable: ties keep first-seen order
|
||||
out.into_iter()
|
||||
.map(|mut g| {
|
||||
g.sort();
|
||||
Json::Arr(g.into_iter().map(Json::Str).collect())
|
||||
g.sort_unstable();
|
||||
Json::Arr(g.into_iter().map(|s| Json::Str(s.to_string())).collect())
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The suggestion lattice: route to an earned cluster, or propose that
|
||||
/// the fact is a new topic.
|
||||
enum Suggestion {
|
||||
Cluster(Vec<String>),
|
||||
NewTopic,
|
||||
}
|
||||
|
||||
impl Suggestion {
|
||||
fn to_json(&self, overlap: usize) -> Json {
|
||||
match self {
|
||||
Suggestion::Cluster(names) => Json::Obj(vec![
|
||||
(
|
||||
"suggestion".into(),
|
||||
Json::Arr(names.iter().cloned().map(Json::Str).collect()),
|
||||
),
|
||||
("reason".into(), Json::Str(format!("shares {overlap} topic(s)"))),
|
||||
]),
|
||||
Suggestion::NewTopic => Json::Obj(vec![
|
||||
("suggestion".into(), Json::Str("new_topic".into())),
|
||||
(
|
||||
"reason".into(),
|
||||
Json::Str("no cluster shares a topic with this fact".into()),
|
||||
),
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Port of suggest(): the cluster a new fact belongs to — or a new topic.
|
||||
pub fn suggest(topology: &Json, fact_topics: &[Json]) -> Json {
|
||||
let cls = clusters(topology, CLUSTER_THRESHOLD);
|
||||
let topic_set = |items: &[Json]| -> Vec<String> {
|
||||
let mut v: Vec<String> = items
|
||||
let mut fact_set: Vec<&str> = fact_topics
|
||||
.iter()
|
||||
.filter_map(|t| match t {
|
||||
Json::Str(s) => Some(s.as_str()),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
fact_set.sort_unstable();
|
||||
fact_set.dedup();
|
||||
let mut best: Option<(Suggestion, usize)> = None;
|
||||
for c in &cls {
|
||||
let names: Vec<&str> = as_arr(c)
|
||||
.iter()
|
||||
.filter_map(|t| match t {
|
||||
Json::Str(s) => Some(s.clone()),
|
||||
.filter_map(|x| match x {
|
||||
Json::Str(s) => Some(s.as_str()),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
v.sort();
|
||||
v.dedup();
|
||||
v
|
||||
};
|
||||
let fact_set = topic_set(fact_topics);
|
||||
let mut best: Option<Vec<String>> = None;
|
||||
let mut best_overlap = 0usize;
|
||||
for c in &cls {
|
||||
let names: Vec<String> = match c {
|
||||
Json::Arr(items) => items
|
||||
.iter()
|
||||
.filter_map(|x| match x {
|
||||
Json::Str(s) => Some(s.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect(),
|
||||
_ => vec![],
|
||||
};
|
||||
let overlap = names
|
||||
.iter()
|
||||
.filter(|n| fact_set.contains(n))
|
||||
.count();
|
||||
if overlap > best_overlap {
|
||||
best_overlap = overlap;
|
||||
best = Some(names);
|
||||
let overlap = names.iter().filter(|n| fact_set.contains(n)).count();
|
||||
if overlap > best.as_ref().map_or(0, |(_, o)| *o) {
|
||||
let cluster = Suggestion::Cluster(names.iter().map(|s| (*s).to_string()).collect());
|
||||
best = Some((cluster, overlap));
|
||||
}
|
||||
}
|
||||
match best {
|
||||
None => Json::Obj(vec![
|
||||
("suggestion".into(), Json::Str("new_topic".into())),
|
||||
(
|
||||
"reason".into(),
|
||||
Json::Str("no cluster shares a topic with this fact".into()),
|
||||
),
|
||||
]),
|
||||
Some(names) => Json::Obj(vec![
|
||||
(
|
||||
"suggestion".into(),
|
||||
Json::Arr(names.into_iter().map(Json::Str).collect()),
|
||||
),
|
||||
(
|
||||
"reason".into(),
|
||||
Json::Str(format!("shares {} topic(s)", best_overlap)),
|
||||
),
|
||||
]),
|
||||
Some((s, overlap)) => s.to_json(overlap),
|
||||
None => Suggestion::NewTopic.to_json(0),
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user