1use crate::prelude::NodeViewOps;
2use ordered_float::OrderedFloat;
3use std::{
4 collections::{hash_map::Iter, HashMap},
5 hash::Hash,
6 marker::PhantomData,
7};
8
9pub trait AsOrd<T: ?Sized + Ord> {
10 fn as_ord(&self) -> &T;
16}
17
18impl<T: Ord> AsOrd<T> for T {
19 fn as_ord(&self) -> &T {
20 self
21 }
22}
23
24impl<T: FloatCore> AsOrd<OrderedFloat<T>> for T {
25 fn as_ord(&self) -> &OrderedFloat<T> {
26 self.into()
27 }
28}
29
30impl<T: FloatCore> AsOrd<(OrderedFloat<T>, OrderedFloat<T>)> for (T, T) {
31 fn as_ord(&self) -> &(OrderedFloat<T>, OrderedFloat<T>) {
32 unsafe { &*(self as *const (T, T) as *const (OrderedFloat<T>, OrderedFloat<T>)) }
34 }
35}
36
37impl<T: FloatCore> AsOrd<Vec<OrderedFloat<T>>> for Vec<T> {
38 fn as_ord(&self) -> &Vec<OrderedFloat<T>> {
39 unsafe { &*(self as *const Vec<T> as *const Vec<OrderedFloat<T>>) }
41 }
42}
43
44pub struct AlgorithmRepr {
49 pub algo_name: String,
50 pub result_type: String,
51}
52
53pub struct AlgorithmResult<G, V, O = V> {
62 pub algo_repr: AlgorithmRepr,
64 pub graph: G,
65 pub result: HashMap<usize, V>,
66 marker: PhantomData<O>,
67}
68
69impl<'graph, G, V, O> AlgorithmResult<G, V, O>
72where
73 G: GraphViewOps<'graph>,
74 V: Clone,
75{
76 pub fn new(graph: G, algo_name: &str, result_type: &str, result: HashMap<usize, V>) -> Self {
85 Self {
87 algo_repr: AlgorithmRepr {
88 algo_name: algo_name.to_string(),
89 result_type: result_type.to_string(),
90 },
91 graph,
92 result,
93 marker: PhantomData,
94 }
95 }
96
97 pub fn get_all_values(&self) -> Vec<V> {
99 self.result.clone().into_values().collect()
100 }
101
102 pub fn get<T: AsNodeRef>(&self, name: T) -> Option<&V> {
107 let v = name.as_node_ref();
108 if self.graph.has_node(v) {
109 let internal_id = self.graph.node(v).unwrap().node.0;
110 self.result.get(&internal_id)
111 } else {
112 None
113 }
114 }
115
116 pub fn get_all_with_names(&self) -> HashMap<String, V> {
121 self.result
122 .iter()
123 .map(|(k, v)| (self.graph.node_name(VID(*k)), v.clone()))
124 .collect()
125 }
126
127 pub fn get_all(&self) -> HashMap<NodeView<G, G>, V> {
132 self.result
133 .iter()
134 .map(|(k, v)| {
135 (
136 NodeView::new_internal(self.graph.clone(), VID(*k)),
137 v.clone(),
138 )
139 })
140 .collect()
141 }
142
143 pub fn sort_by_node(&self, reverse: bool) -> Vec<(NodeView<G, G>, V)> {
153 let mut sorted: Vec<(NodeView<G, G>, V)> = self.get_all().into_iter().collect();
154 sorted.sort_by(|(a, _), (b, _)| if reverse { b.cmp(a) } else { a.cmp(b) });
155 sorted
156 }
157
158 pub fn sort_by_node_name(&self, reverse: bool) -> Vec<(NodeView<G, G>, V)> {
171 let mut sorted: Vec<(NodeView<G, G>, V)> = self.get_all().into_iter().collect();
172 sorted.sort_by(|(a, _), (b, _)| {
173 if reverse {
174 b.name().cmp(&a.name())
175 } else {
176 a.name().cmp(&b.name())
177 }
178 });
179 sorted
180 }
181
182 pub fn iter(&self) -> Iter<'_, usize, V> {
189 self.result.iter()
190 }
191
192 pub fn sort_by_values<F: FnMut(&V, &V) -> std::cmp::Ordering>(
202 &self,
203 mut cmp: F,
204 reverse: bool,
205 ) -> Vec<(NodeView<G, G>, V)> {
206 let mut all_as_vec: Vec<(NodeView<G, G>, V)> = self.get_all().into_iter().collect();
207 all_as_vec.sort_by(|a, b| {
208 let order = cmp(&a.1, &b.1);
209 if reverse {
211 order.reverse()
212 } else {
213 order
214 }
215 });
216 all_as_vec
217 }
218
219 pub fn top_k_by<F: FnMut(&V, &V) -> std::cmp::Ordering>(
234 &self,
235 cmp: F,
236 k: usize,
237 percentage: bool,
238 reverse: bool,
239 ) -> Vec<(NodeView<G, G>, V)> {
240 let k = if percentage {
241 let total_count = self.result.len();
242 (total_count as f64 * (k as f64 / 100.0)) as usize
243 } else {
244 k
245 };
246 self.sort_by_values(cmp, reverse)
247 .into_iter()
248 .take(k)
249 .collect()
250 }
251
252 pub fn min_by<F: FnMut(&V, &V) -> std::cmp::Ordering>(
253 &self,
254 mut cmp: F,
255 ) -> Option<(NodeView<G, G>, V)> {
256 let min_element = self
257 .get_all()
258 .into_iter()
259 .min_by(|(_, a_value), (_, b_value)| cmp(a_value, b_value));
260
261 min_element.map(|(k, v)| (k.clone(), v.clone()))
263 }
264
265 pub fn max_by<F: FnMut(&V, &V) -> std::cmp::Ordering>(
266 &self,
267 mut cmp: F,
268 ) -> Option<(NodeView<G, G>, V)> {
269 let max_element = self
270 .get_all()
271 .into_iter()
272 .max_by(|(_, a_value), (_, b_value)| cmp(a_value, b_value));
273
274 max_element.map(|(k, v)| (k.clone(), v.clone()))
276 }
277
278 pub fn median_by<F: FnMut(&V, &V) -> std::cmp::Ordering>(
279 &self,
280 mut cmp: F,
281 ) -> Option<(NodeView<G, G>, V)> {
282 let mut items: Vec<(NodeView<G, G>, V)> = self.get_all().into_iter().collect();
284 let len = items.len();
285 if len == 0 {
286 return None;
287 }
288
289 items.sort_by(|(_, a_value), (_, b_value)| cmp(a_value, b_value));
290 let median_index = len / 2;
291
292 Some((items[median_index].0.clone(), items[median_index].1.clone()))
293 }
294
295 pub fn len(&self) -> usize {
296 self.result.len()
297 }
298}
299
300impl<'graph, G, V, O> AlgorithmResult<G, V, O>
301where
302 G: GraphViewOps<'graph>,
303 V: Clone,
304 O: Ord,
305 V: AsOrd<O>,
306{
307 pub fn group_by(&self) -> HashMap<V, Vec<String>>
308 where
309 V: Eq + Hash,
310 {
311 let mut groups: HashMap<V, Vec<String>> = HashMap::new();
312
313 for node in self.graph.nodes() {
314 if let Some(value) = self.result.get(&node.node.0) {
315 let entry = groups.entry(value.clone()).or_default();
316 entry.push(node.name().to_string());
317 }
318 }
319 groups
320 }
321
322 pub fn sort_by_value(&self, reverse: bool) -> Vec<(NodeView<G, G>, V)> {
332 self.sort_by_values(|a, b| O::cmp(a.as_ord(), b.as_ord()), reverse)
333 }
334
335 pub fn top_k(&self, k: usize, percentage: bool, reverse: bool) -> Vec<(NodeView<G, G>, V)> {
350 self.top_k_by(
351 |a, b| O::cmp(a.as_ord(), b.as_ord()),
352 k,
353 percentage,
354 reverse,
355 )
356 }
357
358 pub fn min(&self) -> Option<(NodeView<G, G>, V)> {
360 self.min_by(|a, b| O::cmp(a.as_ord(), b.as_ord()))
361 }
362
363 pub fn max(&self) -> Option<(NodeView<G, G>, V)> {
365 self.max_by(|a, b| O::cmp(a.as_ord(), b.as_ord()))
366 }
367
368 pub fn median(&self) -> Option<(NodeView<G, G>, V)> {
370 self.median_by(|a, b| O::cmp(a.as_ord(), b.as_ord()))
371 }
372}
373
374use crate::{
375 core::entities::{nodes::node_ref::AsNodeRef, VID},
376 db::graph::node::NodeView,
377 prelude::GraphViewOps,
378};
379use num_traits::float::FloatCore;
380use std::fmt;
381
382impl<'graph, G: GraphViewOps<'graph>, V: fmt::Debug, O> fmt::Display for AlgorithmResult<G, V, O> {
383 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
384 writeln!(f, "AlgorithmResultNew {{")?;
385 writeln!(f, " Algorithm Name: {}", self.algo_repr.algo_name)?;
386 writeln!(f, " Result Type: {}", self.algo_repr.result_type)?;
387 writeln!(f, " Number of Nodes: {}", self.result.len())?;
388 writeln!(f, " Results: [")?;
389
390 for node in self.graph.nodes().iter() {
391 let value = self.result.get(&node.node.0);
392 writeln!(f, " {}: {:?}", node.name(), value)?;
393 }
394
395 writeln!(f, " ]")?;
396 writeln!(f, "}}")
397 }
398}
399
400impl<'graph, G: GraphViewOps<'graph>, V: fmt::Debug, O> fmt::Debug for AlgorithmResult<G, V, O> {
401 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
402 writeln!(f, "AlgorithmResultNew {{")?;
403 writeln!(f, " Algorithm Name: {:?}", self.algo_repr.algo_name)?;
404 writeln!(f, " Result Type: {:?}", self.algo_repr.result_type)?;
405 writeln!(f, " Number of Nodes: {:?}", self.result.len())?;
406 writeln!(f, " Results: [")?;
407
408 for node in self.graph.nodes().iter() {
409 let value = self.result.get(&node.node.0);
410 writeln!(f, " {:?}: {:?}", node.name(), value)?;
411 }
412
413 writeln!(f, " ]")?;
414 writeln!(f, "}}")
415 }
416}
417
418#[cfg(test)]
420mod algorithm_result_test {
421 use super::*;
422 use crate::{
423 algorithms::components::weakly_connected_components,
424 db::{api::mutation::AdditionOps, graph::graph::Graph},
425 prelude::{NO_PROPS, *},
426 };
427 use ordered_float::OrderedFloat;
428 use raphtory_api::core::{entities::GID, utils::logging::global_info_logger};
429 use tracing::info;
430
431 fn create_algo_result_u64() -> AlgorithmResult<Graph, u64> {
432 let g = create_graph();
433 let mut map: HashMap<usize, u64> = HashMap::new();
434 map.insert(g.node("A").unwrap().node.0, 10);
435 map.insert(g.node("B").unwrap().node.0, 20);
436 map.insert(g.node("C").unwrap().node.0, 30);
437 let results_type = std::any::type_name::<u64>();
438 AlgorithmResult::new(g, "create_algo_result_u64_test", results_type, map)
439 }
440
441 fn create_graph() -> Graph {
442 let g = Graph::new();
443 g.add_node(0, "A", NO_PROPS, None)
444 .expect("Could not add node to graph");
445 g.add_node(0, "B", NO_PROPS, None)
446 .expect("Could not add node to graph");
447 g.add_node(0, "C", NO_PROPS, None)
448 .expect("Could not add node to graph");
449 g.add_node(0, "D", NO_PROPS, None)
450 .expect("Could not add node to graph");
451 g
452 }
453
454 fn group_by_test() -> AlgorithmResult<Graph, u64> {
455 let g = create_graph();
456 let mut map: HashMap<usize, u64> = HashMap::new();
457 map.insert(g.node("A").unwrap().node.0, 10);
458 map.insert(g.node("B").unwrap().node.0, 20);
459 map.insert(g.node("C").unwrap().node.0, 30);
460 map.insert(g.node("D").unwrap().node.0, 10);
461 let results_type = std::any::type_name::<u64>();
462 AlgorithmResult::new(g, "group_by_test", results_type, map)
463 }
464
465 fn create_algo_result_f64() -> AlgorithmResult<Graph, f64, OrderedFloat<f64>> {
466 let g = Graph::new();
467 g.add_node(0, "A", NO_PROPS, None)
468 .expect("Could not add node to graph");
469 g.add_node(0, "B", NO_PROPS, None)
470 .expect("Could not add node to graph");
471 g.add_node(0, "C", NO_PROPS, None)
472 .expect("Could not add node to graph");
473 g.add_node(0, "D", NO_PROPS, None)
474 .expect("Could not add node to graph");
475 let mut map: HashMap<usize, f64> = HashMap::new();
476 map.insert(g.node("A").unwrap().node.0, 10.0);
477 map.insert(g.node("B").unwrap().node.0, 20.0);
478 map.insert(g.node("C").unwrap().node.0, 30.0);
479 let results_type = std::any::type_name::<f64>();
480 AlgorithmResult::new(g, "create_algo_result_u64_test", results_type, map)
481 }
482
483 fn create_algo_result_tuple(
484 ) -> AlgorithmResult<Graph, (f32, f32), (OrderedFloat<f32>, OrderedFloat<f32>)> {
485 let g = create_graph();
486 let mut res: HashMap<usize, (f32, f32)> = HashMap::new();
487 res.insert(g.node("A").unwrap().node.0, (10.0, 20.0));
488 res.insert(g.node("B").unwrap().node.0, (20.0, 30.0));
489 res.insert(g.node("C").unwrap().node.0, (30.0, 40.0));
490 let results_type = std::any::type_name::<(f32, f32)>();
491 AlgorithmResult::new(g, "create_algo_result_tuple", results_type, res)
492 }
493
494 fn create_algo_result_hashmap_vec() -> AlgorithmResult<Graph, Vec<(i32, String)>> {
495 let g = create_graph();
496 let mut res: HashMap<usize, Vec<(i32, String)>> = HashMap::new();
497 res.insert(g.node("A").unwrap().node.0, vec![(11, "H".to_string())]);
498 res.insert(g.node("B").unwrap().node.0, vec![]);
499 res.insert(
500 g.node("C").unwrap().node.0,
501 vec![(22, "E".to_string()), (33, "F".to_string())],
502 );
503 let results_type = std::any::type_name::<(i32, String)>();
504 AlgorithmResult::new(g, "create_algo_result_hashmap_vec", results_type, res)
505 }
506
507 #[test]
508 fn test_min_max_value() {
509 let algo_result = create_algo_result_u64();
510 let v_a = algo_result.graph.node("A".to_string()).unwrap();
511 let v_b = algo_result.graph.node("B".to_string()).unwrap();
512 let v_c = algo_result.graph.node("C".to_string()).unwrap();
513 assert_eq!(algo_result.min(), Some((v_a.clone(), 10u64)));
514 assert_eq!(algo_result.max(), Some((v_c.clone(), 30u64)));
515 assert_eq!(algo_result.median(), Some((v_b.clone(), 20u64)));
516 let algo_result = create_algo_result_f64();
517 assert_eq!(algo_result.min(), Some((v_a, 10.0)));
518 assert_eq!(algo_result.max(), Some((v_c, 30.0)));
519 assert_eq!(algo_result.median(), Some((v_b, 20.0)));
520 }
521
522 #[test]
523 fn test_get() {
524 let algo_result = create_algo_result_u64();
525 let node_c = algo_result.graph.node("C").unwrap();
526 let node_d = algo_result.graph.node("D").unwrap();
527 assert_eq!(algo_result.get(node_c.clone()), Some(&30u64));
528 assert_eq!(algo_result.get(node_d.clone()), None);
529 let algo_result = create_algo_result_f64();
530 assert_eq!(algo_result.get(node_c.clone()), Some(&30.0f64));
531 let algo_result = create_algo_result_tuple();
532 assert_eq!(algo_result.get(node_c.clone()).unwrap().0, 30.0f32);
533 let algo_result = create_algo_result_hashmap_vec();
534 let answer = algo_result.get(node_c.clone()).unwrap().first().unwrap().0;
535 assert_eq!(answer, 22i32);
536 }
537
538 #[test]
539 fn test_sort() {
540 let algo_result = create_algo_result_u64();
541 let sorted = algo_result.sort_by_value(true);
542 let v_c = algo_result.graph.node("C").unwrap();
543 let v_b = algo_result.graph.node("B").unwrap();
544 let v_a = algo_result.graph.node("A").unwrap();
545 assert_eq!(sorted[0].0, v_c.clone());
546 let sorted = algo_result.sort_by_value(false);
547 assert_eq!(sorted[0].0, v_a.clone());
548
549 let algo_result = create_algo_result_f64();
550 let sorted = algo_result.sort_by_value(true);
551 assert_eq!(sorted[0].0, v_c.clone());
552 let sorted = algo_result.sort_by_value(false);
553 assert_eq!(sorted[0].0, v_a);
554 assert_eq!(sorted[1].0, v_b);
555
556 let algo_result = create_algo_result_tuple();
557 assert_eq!(algo_result.sort_by_value(true)[0].0, v_c.clone());
558
559 let algo_result = create_algo_result_hashmap_vec();
560 assert_eq!(algo_result.sort_by_value(true)[0].0, v_c);
561 }
562
563 #[test]
564 fn test_top_k() {
565 let algo_result = create_algo_result_u64();
566 let v_c = algo_result.graph.node("C").unwrap();
567 let v_a = algo_result.graph.node("A").unwrap();
568 let v_b = algo_result.graph.node("B").unwrap();
569
570 let top_k = algo_result.top_k(2, false, false);
571 assert_eq!(top_k[0].0, v_a.clone());
572 let top_k = algo_result.top_k(2, false, true);
573 assert_eq!(top_k[0].0, v_c.clone());
574
575 let algo_result = create_algo_result_f64();
576 let top_k = algo_result.top_k(2, false, false);
577 assert_eq!(top_k[0].0, v_a.clone());
578 assert_eq!(top_k[1].0, v_b.clone());
579 let top_k = algo_result.top_k(2, false, true);
580 assert_eq!(top_k[0].0, v_c);
581
582 let algo_result = create_algo_result_tuple();
583 assert_eq!(algo_result.top_k(2, false, false)[0].0, v_a.clone());
584 assert_eq!(algo_result.top_k(2, false, false)[1].0, v_b.clone());
585
586 let algo_result = create_algo_result_hashmap_vec();
587 assert_eq!(algo_result.top_k(2, false, false)[0].0, v_b);
588 assert_eq!(algo_result.top_k(2, false, false)[1].0, v_a);
589 }
590
591 #[test]
592 fn test_group_by() {
593 let algo_result = group_by_test();
594 let grouped = algo_result.group_by();
595 assert_eq!(grouped.get(&10).unwrap().len(), 2);
596 assert!(grouped.get(&10).unwrap().contains(&"A".to_string()));
597 assert!(!grouped.get(&10).unwrap().contains(&"B".to_string()));
598
599 let algo_result = create_algo_result_hashmap_vec();
600 assert_eq!(
601 algo_result
602 .group_by()
603 .get(&vec![(11, "H".to_string())])
604 .unwrap()
605 .len(),
606 1
607 );
608 }
609
610 #[test]
611 fn test_get_all_with_names() {
612 let algo_result = create_algo_result_u64();
613 let all = algo_result.get_all_values();
614 assert_eq!(all.len(), 3);
615
616 let algo_result = create_algo_result_f64();
617 let all = algo_result.get_all_values();
618 assert_eq!(all.len(), 3);
619
620 let algo_result = create_algo_result_tuple();
621 let algo_results_hashmap = algo_result.get_all_with_names();
622 let tuple_result = algo_results_hashmap.get("A").unwrap();
623 assert_eq!(tuple_result.0, 10.0);
624 assert_eq!(algo_result.get_all_values().len(), 3);
625
626 let algo_result = create_algo_result_hashmap_vec();
627 let algo_results_hashmap = algo_result.get_all_with_names();
628 let tuple_result = algo_results_hashmap.get("A").unwrap();
629 assert_eq!(tuple_result.clone().first().unwrap().0, 11);
630 assert_eq!(algo_result.get_all_values().len(), 3);
631 }
632
633 #[test]
634 fn test_sort_by_node() {
635 let algo_result = create_algo_result_u64();
636 let v_c = algo_result.graph.node("C").unwrap();
637 let v_a = algo_result.graph.node("A").unwrap();
638 let v_b = algo_result.graph.node("B").unwrap();
639 let sorted = algo_result.sort_by_node(true);
640 let my_array: Vec<(NodeView<Graph, Graph>, u64)> =
641 vec![(v_c, 30u64), (v_b, 20u64), (v_a, 10u64)];
642 assert_eq!(my_array, sorted);
643
644 let algo_result = create_algo_result_f64();
645 let v_c = algo_result.graph.node("C").unwrap();
646 let v_a = algo_result.graph.node("A").unwrap();
647 let v_b = algo_result.graph.node("B").unwrap();
648 let sorted = algo_result.sort_by_node(true);
649 let my_array: Vec<(NodeView<Graph, Graph>, f64)> =
650 vec![(v_c, 30.0), (v_b, 20.0), (v_a, 10.0)];
651 assert_eq!(my_array, sorted);
652
653 let algo_result = create_algo_result_tuple();
654 let v_c = algo_result.graph.node("C").unwrap();
655 let v_a = algo_result.graph.node("A").unwrap();
656 let v_b = algo_result.graph.node("B").unwrap();
657
658 let sorted = algo_result.sort_by_node(true);
659 let my_array: Vec<(NodeView<Graph, Graph>, (f32, f32))> = vec![
660 (v_c, (30.0, 40.0)),
661 (v_b, (20.0, 30.0)),
662 (v_a, (10.0, 20.0)),
663 ];
664 assert_eq!(my_array, sorted);
665 let algo_result = create_algo_result_hashmap_vec();
667 let v_c = algo_result.graph.node("C").unwrap();
668 let v_a = algo_result.graph.node("A").unwrap();
669 let v_b = algo_result.graph.node("B").unwrap();
670
671 let sorted = algo_result.sort_by_node(true);
672 let vec_c = vec![(22, "E".to_string()), (33, "F".to_string())];
673 let vec_b = vec![];
674 let vec_a = vec![(11, "H".to_string())];
675 let my_array: Vec<(NodeView<Graph, Graph>, Vec<(i32, String)>)> =
676 vec![(v_c, vec_c), (v_b, vec_b), (v_a, vec_a)];
677 assert_eq!(my_array, sorted);
678 }
679
680 #[test]
681 fn test_get_all() {
682 global_info_logger();
683 let algo_result = create_algo_result_u64();
684 let gotten_all = algo_result.get_all();
685 let names: Vec<String> = gotten_all.keys().map(|vv| vv.name()).collect();
686 info!("{:?}", names)
687 }
688
689 #[test]
690 fn test_windowed_graph() {
691 let g = Graph::new();
692 g.add_edge(0, 1, 2, NO_PROPS, Some("ZERO-TWO"))
693 .expect("Unable to add edge");
694 g.add_edge(1, 1, 3, NO_PROPS, Some("ZERO-TWO"))
695 .expect("Unable to add edge");
696 g.add_edge(2, 4, 5, NO_PROPS, Some("ZERO-TWO"))
697 .expect("Unable to add edge");
698 g.add_edge(3, 6, 7, NO_PROPS, Some("THREE-FIVE"))
699 .expect("Unable to add edge");
700 g.add_edge(4, 8, 9, NO_PROPS, Some("THREE-FIVE"))
701 .expect("Unable to add edge");
702 let g_layer = g.layers(vec!["ZERO-TWO"]).unwrap();
703 let res_window = weakly_connected_components(&g_layer, 20, None);
704 let mut expected_result = HashMap::new();
705 expected_result.insert("8".to_string(), GID::U64(8));
706 expected_result.insert("1".to_string(), GID::U64(1));
707 expected_result.insert("3".to_string(), GID::U64(1));
708 expected_result.insert("2".to_string(), GID::U64(1));
709 expected_result.insert("5".to_string(), GID::U64(4));
710 expected_result.insert("6".to_string(), GID::U64(6));
711 expected_result.insert("7".to_string(), GID::U64(7));
712 expected_result.insert("4".to_string(), GID::U64(4));
713 expected_result.insert("9".to_string(), GID::U64(9));
714 assert_eq!(res_window.get_all_with_names(), expected_result);
715 }
716}