qualia_core_db/modalities/
capability_gap.rs1fn holds(cap: u64, held: &[u64], equivalences: &[(u64, u64)]) -> bool {
12 held.contains(&cap)
13 || equivalences
14 .iter()
15 .any(|&(req, h)| req == cap && held.contains(&h))
16}
17
18pub fn capability_gap(
21 required: &[u64],
22 held: &[u64],
23 equivalences: &[(u64, u64)],
24 out: &mut [u64],
25) -> usize {
26 let mut n = 0usize;
27 for &cap in required {
28 if !holds(cap, held, equivalences) {
29 if n >= out.len() {
30 break;
31 }
32 out[n] = cap;
33 n += 1;
34 }
35 }
36 n
37}
38
39pub fn requirements_met(required: &[u64], held: &[u64], equivalences: &[(u64, u64)]) -> bool {
41 required.iter().all(|&c| holds(c, held, equivalences))
42}
43
44#[inline]
47pub fn meets_competency_level(held_level: u8, required_level: u8) -> bool {
48 held_level >= required_level
49}
50
51pub const MAX_CAP_NODES: usize = 64;
55
56pub fn learning_path_cost(
62 nodes: &[u64],
63 edges: &[(u64, u64, u32)],
64 held: &[u64],
65 goal: u64,
66) -> Option<u32> {
67 let n = nodes.len();
68 if n == 0 || n > MAX_CAP_NODES {
69 return None;
70 }
71 let idx = |id: u64| nodes.iter().position(|&x| x == id);
72 let goal_i = idx(goal)?;
73 let mut dist = [u32::MAX; MAX_CAP_NODES];
74 let mut done = [false; MAX_CAP_NODES];
75 for &h in held {
77 if let Some(hi) = idx(h) {
78 dist[hi] = 0;
79 }
80 }
81 for _ in 0..n {
82 let mut u = usize::MAX;
84 let mut best = u32::MAX;
85 for i in 0..n {
86 if !done[i] && dist[i] < best {
87 best = dist[i];
88 u = i;
89 }
90 }
91 if u == usize::MAX {
92 break; }
94 done[u] = true;
95 if u == goal_i {
96 return Some(dist[u]);
97 }
98 for &(from, to, cost) in edges {
100 if from == nodes[u] {
101 if let Some(ti) = idx(to) {
102 let nd = dist[u].saturating_add(cost);
103 if nd < dist[ti] {
104 dist[ti] = nd;
105 }
106 }
107 }
108 }
109 }
110 if dist[goal_i] == u32::MAX {
111 None
112 } else {
113 Some(dist[goal_i])
114 }
115}
116
117pub fn estimate_capability(prior: f32, present: u32, total: u32) -> f32 {
123 if total == 0 {
124 return prior;
125 }
126 let l = present as f32 / total as f32;
127 let num = prior * l;
128 let den = num + (1.0 - prior) * (1.0 - l);
129 if den < 1e-9 {
130 prior
131 } else {
132 num / den
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use crate::q_hash;
140
141 #[test]
142 fn competency_levels_and_bayesian_estimate() {
143 assert!(meets_competency_level(5, 3));
144 assert!(!meets_competency_level(2, 3));
145 assert!((estimate_capability(0.4, 0, 0) - 0.4).abs() < 1e-6);
147 assert!(estimate_capability(0.5, 9, 10) > 0.5);
149 assert!(estimate_capability(0.5, 1, 10) < 0.5);
150 }
151
152 #[test]
153 fn a_star_finds_the_shortest_learning_path() {
154 let (welding, fab, robotics) = (
156 q_hash("cap:welding"),
157 q_hash("cap:fabrication"),
158 q_hash("cap:robotics"),
159 );
160 let nodes = [welding, fab, robotics];
161 let edges = [
162 (welding, fab, 2u32),
163 (fab, robotics, 3u32),
164 (welding, robotics, 10u32),
165 ];
166 assert_eq!(
168 learning_path_cost(&nodes, &edges, &[welding], robotics),
169 Some(5)
170 );
171 assert_eq!(
173 learning_path_cost(&nodes, &edges, &[robotics], robotics),
174 Some(0)
175 );
176 let isolated = q_hash("cap:isolated");
178 assert_eq!(
179 learning_path_cost(&[welding, isolated], &edges, &[welding], isolated),
180 None
181 );
182 }
183
184 #[test]
185 fn gap_is_the_set_difference() {
186 let (welding, wiring, plumbing) = (
187 q_hash("cap:welding"),
188 q_hash("cap:wiring"),
189 q_hash("cap:plumbing"),
190 );
191 let required = [welding, wiring, plumbing];
192 let held = [welding];
193 let mut out = [0u64; 8];
194 let n = capability_gap(&required, &held, &[], &mut out);
195 assert_eq!(n, 2);
196 assert!(out[..n].contains(&wiring) && out[..n].contains(&plumbing));
197 assert!(!requirements_met(&required, &held, &[]));
198 }
199
200 #[test]
201 fn experiential_equivalence_closes_the_gap() {
202 let formal = q_hash("cap:formalDegree");
203 let experiential = q_hash("cap:apprenticeship");
204 let required = [formal];
205 let held = [experiential];
206 assert_eq!(capability_gap(&required, &held, &[], &mut [0u64; 4]), 1);
208 let equiv = [(formal, experiential)];
210 assert_eq!(capability_gap(&required, &held, &equiv, &mut [0u64; 4]), 0);
211 assert!(requirements_met(&required, &held, &equiv));
212 }
213}