vyre_primitives/graph/
alias_registry.rs1use vyre_foundation::ir::DataType;
7
8pub const ALIAS_UNION_OP_ID: &str = "vyre-primitives::graph::alias_union";
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct AliasOpDescriptor {
14 pub inputs: Vec<DataType>,
16 pub output: DataType,
18 pub description: &'static str,
20 pub commutative: bool,
22 pub side_effects: bool,
24}
25
26impl AliasOpDescriptor {
27 #[must_use]
29 pub fn alias_union() -> Self {
30 Self {
31 inputs: vec![DataType::U32, DataType::U32],
32 output: DataType::U32,
33 description: "Lock-free warp-accelerated union-find alias join",
34 commutative: true,
35 side_effects: true,
36 }
37 }
38}
39
40#[derive(Debug, Default, Clone)]
42pub struct AliasRegistry {
43 alias_union: Option<AliasOpDescriptor>,
44 extension_ops: Vec<(&'static str, AliasOpDescriptor)>,
45}
46
47impl AliasRegistry {
48 pub fn register(&mut self, op_id: &'static str, descriptor: AliasOpDescriptor) {
50 if op_id == ALIAS_UNION_OP_ID {
51 self.alias_union = Some(descriptor);
52 return;
53 }
54 match self
55 .extension_ops
56 .binary_search_by(|(registered, _)| registered.cmp(&op_id))
57 {
58 Ok(index) => self.extension_ops[index].1 = descriptor,
59 Err(index) => self.extension_ops.insert(index, (op_id, descriptor)),
60 }
61 }
62
63 #[must_use]
65 pub fn get(&self, op_id: &str) -> Option<&AliasOpDescriptor> {
66 if op_id == ALIAS_UNION_OP_ID {
67 return self.alias_union.as_ref();
68 }
69 self.extension_ops
70 .binary_search_by(|(registered, _)| registered.cmp(&op_id))
71 .ok()
72 .map(|index| &self.extension_ops[index].1)
73 }
74
75 #[must_use]
77 pub fn contains(&self, op_id: &str) -> bool {
78 self.get(op_id).is_some()
79 }
80
81 #[must_use]
83 pub fn len(&self) -> usize {
84 usize::from(self.alias_union.is_some()) + self.extension_ops.len()
85 }
86
87 #[must_use]
89 pub fn is_empty(&self) -> bool {
90 self.alias_union.is_none() && self.extension_ops.is_empty()
91 }
92
93 #[must_use]
98 pub fn registered_op_ids(&self) -> Vec<&'static str> {
99 let mut ids = Vec::with_capacity(self.len());
100 if self.alias_union.is_some() {
101 ids.push(ALIAS_UNION_OP_ID);
102 }
103 ids.extend(self.extension_ops.iter().map(|(op_id, _)| *op_id));
104 ids
105 }
106}
107
108pub fn register_alias_ops(registry: &mut AliasRegistry) {
112 registry.register(ALIAS_UNION_OP_ID, AliasOpDescriptor::alias_union());
113}
114
115#[must_use]
117pub fn default_alias_registry() -> AliasRegistry {
118 let mut registry = AliasRegistry::default();
119 register_alias_ops(&mut registry);
120 registry
121}
122
123#[must_use]
125pub fn alias_union_registered(registry: &AliasRegistry) -> bool {
126 registry.contains(ALIAS_UNION_OP_ID)
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn default_registry_contains_alias_union_only() {
135 let registry = default_alias_registry();
136 assert_eq!(registry.len(), 1);
137 assert!(alias_union_registered(®istry));
138 }
139
140 #[test]
141 fn empty_registry_has_no_implicit_alias_union() {
142 let registry = AliasRegistry::default();
143 assert!(registry.is_empty());
144 assert!(!alias_union_registered(®istry));
145 assert!(!registry.contains(ALIAS_UNION_OP_ID));
146 }
147
148 #[test]
149 fn alias_union_descriptor_contract_is_pinned() {
150 let registry = default_alias_registry();
151 let desc = registry
152 .get(ALIAS_UNION_OP_ID)
153 .expect("Fix: default registry must contain alias-union descriptor");
154 assert_eq!(desc.inputs, vec![DataType::U32, DataType::U32]);
155 assert_eq!(desc.output, DataType::U32);
156 assert!(desc.commutative, "alias-union must be commutative");
157 assert!(desc.side_effects, "alias-union mutates union-find state");
158 }
159
160 #[test]
161 fn extension_registration_updates_without_duplicating_entries() {
162 let mut registry = default_alias_registry();
163 let mut descriptor = AliasOpDescriptor::alias_union();
164 descriptor.description = "test extension alias op";
165 descriptor.commutative = false;
166 descriptor.side_effects = false;
167
168 registry.register("vyre-primitives::graph::alias_test_ext", descriptor.clone());
169 registry.register("vyre-primitives::graph::alias_test_ext", descriptor);
170
171 assert_eq!(registry.len(), 2);
172 let ext = registry
173 .get("vyre-primitives::graph::alias_test_ext")
174 .expect("Fix: extension alias op should be registered");
175 assert_eq!(ext.description, "test extension alias op");
176 assert!(!ext.commutative);
177 assert!(!ext.side_effects);
178 assert!(alias_union_registered(®istry));
179 }
180
181 #[test]
182 fn extension_ops_are_kept_sorted_for_binary_lookup() {
183 let mut registry = default_alias_registry();
184 for op_id in [
185 "vyre-primitives::graph::alias_z",
186 "vyre-primitives::graph::alias_a",
187 "vyre-primitives::graph::alias_m",
188 "vyre-primitives::graph::alias_b",
189 ] {
190 registry.register(op_id, AliasOpDescriptor::alias_union());
191 }
192
193 let ids = registry
194 .extension_ops
195 .iter()
196 .map(|(op_id, _)| *op_id)
197 .collect::<Vec<_>>();
198 assert_eq!(
199 ids,
200 vec![
201 "vyre-primitives::graph::alias_a",
202 "vyre-primitives::graph::alias_b",
203 "vyre-primitives::graph::alias_m",
204 "vyre-primitives::graph::alias_z",
205 ],
206 "Fix: alias extension registry must stay sorted so lookup is binary-searchable."
207 );
208 assert!(registry.get("vyre-primitives::graph::alias_m").is_some());
209 assert!(registry
210 .get("vyre-primitives::graph::alias_missing")
211 .is_none());
212 }
213
214 #[test]
215 fn duplicate_extension_update_preserves_sorted_registry_position() {
216 let mut registry = default_alias_registry();
217 registry.register(
218 "vyre-primitives::graph::alias_c",
219 AliasOpDescriptor::alias_union(),
220 );
221 registry.register(
222 "vyre-primitives::graph::alias_a",
223 AliasOpDescriptor::alias_union(),
224 );
225 let mut updated = AliasOpDescriptor::alias_union();
226 updated.description = "updated alias op";
227 updated.commutative = false;
228 registry.register("vyre-primitives::graph::alias_c", updated);
229
230 assert_eq!(registry.len(), 3);
231 assert_eq!(
232 registry.extension_ops[0].0,
233 "vyre-primitives::graph::alias_a"
234 );
235 assert_eq!(
236 registry.extension_ops[1].0,
237 "vyre-primitives::graph::alias_c"
238 );
239 let desc = registry
240 .get("vyre-primitives::graph::alias_c")
241 .expect("Fix: updated alias_c descriptor must remain registered");
242 assert_eq!(desc.description, "updated alias op");
243 assert!(!desc.commutative);
244 }
245
246 #[test]
247 fn registered_op_ids_are_deterministic_and_do_not_expose_descriptors() {
248 let mut registry = default_alias_registry();
249 registry.register(
250 "vyre-primitives::graph::alias_z",
251 AliasOpDescriptor::alias_union(),
252 );
253 registry.register(
254 "vyre-primitives::graph::alias_a",
255 AliasOpDescriptor::alias_union(),
256 );
257
258 assert_eq!(
259 registry.registered_op_ids(),
260 vec![
261 ALIAS_UNION_OP_ID,
262 "vyre-primitives::graph::alias_a",
263 "vyre-primitives::graph::alias_z",
264 ]
265 );
266 }
267}