1#![allow(missing_docs)]
8
9use crate::algorithms::community::{label_propagation_result, louvain_communities_result};
10use crate::algorithms::connectivity::connected_components;
11use crate::algorithms::floyd_warshall;
12use crate::algorithms::{betweenness_centrality, closeness_centrality};
13use crate::base::Graph;
14use crate::error::{GraphError, Result};
15use crate::generators::{barabasi_albert_graph, erdos_renyi_graph};
16use crate::measures::pagerank_centrality;
17use crate::advanced::{create_enhanced_advanced_processor, execute_with_enhanced_advanced};
22use std::collections::{HashMap, HashSet};
23use std::time::{Duration, Instant, SystemTime};
24
25#[derive(Debug, Clone)]
27pub struct ValidationTolerances {
28 pub absolute_tolerance: f64,
30 pub relative_tolerance: f64,
32 pub integer_tolerance: i32,
34 pub correlation_threshold: f64,
36 pub centrality_deviation_threshold: f64,
38}
39
40impl Default for ValidationTolerances {
41 fn default() -> Self {
42 Self {
43 absolute_tolerance: 1e-6,
44 relative_tolerance: 1e-5,
45 integer_tolerance: 0,
46 correlation_threshold: 0.95,
47 centrality_deviation_threshold: 0.01,
48 }
49 }
50}
51
52#[derive(Debug, Clone)]
54pub struct ValidationTestCase {
55 pub name: String,
57 pub graph_generator: GraphGenerator,
59 pub algorithms: Vec<ValidationAlgorithm>,
61 pub tolerances: ValidationTolerances,
63 pub num_runs: usize,
65}
66
67#[derive(Debug, Clone)]
69pub enum GraphGenerator {
70 Random {
71 nodes: usize,
72 edges: usize,
73 directed: bool,
74 },
75 ErdosRenyi {
76 nodes: usize,
77 probability: f64,
78 },
79 BarabasiAlbert {
80 nodes: usize,
81 edges_per_node: usize,
82 },
83 SmallWorld {
84 nodes: usize,
85 k: usize,
86 p: f64,
87 },
88 Complete {
89 nodes: usize,
90 },
91 Custom {
92 generator: fn() -> Result<Graph<usize, f64>>,
93 },
94}
95
96#[derive(Debug, Clone, PartialEq)]
98pub enum ValidationAlgorithm {
99 ConnectedComponents,
100 StronglyConnectedComponents,
101 PageRank {
102 damping: f64,
103 max_iterations: usize,
104 tolerance: f64,
105 },
106 BetweennessCentrality,
107 ClosenessCentrality,
108 DegreeCentrality,
109 ShortestPaths {
110 source: usize,
111 },
112 AllPairsShortestPaths,
113 LouvainCommunities,
114 LabelPropagation {
115 max_iterations: usize,
116 },
117}
118
119impl std::hash::Hash for ValidationAlgorithm {
120 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
121 match self {
122 ValidationAlgorithm::ConnectedComponents => 0.hash(state),
123 ValidationAlgorithm::StronglyConnectedComponents => 1.hash(state),
124 ValidationAlgorithm::PageRank { max_iterations, .. } => {
125 2.hash(state);
126 max_iterations.hash(state);
127 }
129 ValidationAlgorithm::BetweennessCentrality => 3.hash(state),
130 ValidationAlgorithm::ClosenessCentrality => 4.hash(state),
131 ValidationAlgorithm::DegreeCentrality => 5.hash(state),
132 ValidationAlgorithm::ShortestPaths { source } => {
133 6.hash(state);
134 source.hash(state);
135 }
136 ValidationAlgorithm::AllPairsShortestPaths => 7.hash(state),
137 ValidationAlgorithm::LouvainCommunities => 8.hash(state),
138 ValidationAlgorithm::LabelPropagation { max_iterations } => {
139 9.hash(state);
140 max_iterations.hash(state);
141 }
142 }
143 }
144}
145
146impl Eq for ValidationAlgorithm {}
147
148#[derive(Debug, Clone)]
150pub struct ValidationResult {
151 pub algorithm: ValidationAlgorithm,
153 pub test_case: String,
155 pub passed: bool,
157 pub accuracy_score: f64,
159 pub standard_time: Duration,
161 pub advanced_time: Duration,
163 pub speedup_factor: f64,
165 pub metrics: ValidationMetrics,
167 pub error_message: Option<String>,
169}
170
171#[derive(Debug, Clone)]
173pub struct ValidationMetrics {
174 pub max_absolute_error: f64,
176 pub mean_absolute_error: f64,
178 pub root_mean_square_error: f64,
180 pub pearson_correlation: f64,
182 pub spearman_correlation: f64,
184 pub elements_compared: usize,
186 pub exact_matches: usize,
188 pub custom_metrics: HashMap<String, f64>,
190}
191
192#[derive(Debug)]
194pub struct ValidationReport {
195 pub summary: ValidationSummary,
197 pub test_results: Vec<ValidationResult>,
199 pub performance_analysis: PerformanceAnalysis,
201 pub accuracy_analysis: AccuracyAnalysis,
203 pub recommendations: Vec<String>,
205 pub timestamp: SystemTime,
207}
208
209#[derive(Debug, Clone)]
211pub struct ValidationSummary {
212 pub total_tests: usize,
214 pub tests_passed: usize,
216 pub pass_rate: f64,
218 pub average_accuracy: f64,
220 pub average_speedup: f64,
222 pub total_time: Duration,
224}
225
226#[derive(Debug, Clone)]
228pub struct PerformanceAnalysis {
229 pub best_speedup: f64,
231 pub worst_speedup: f64,
233 pub top_performers: Vec<(ValidationAlgorithm, f64)>,
235 pub performance_regressions: Vec<(ValidationAlgorithm, f64)>,
237 pub memory_efficiency: f64,
239}
240
241#[derive(Debug, Clone)]
243pub struct AccuracyAnalysis {
244 pub best_accuracy: f64,
246 pub worst_accuracy: f64,
248 pub perfect_accuracy_algorithms: Vec<ValidationAlgorithm>,
250 pub accuracy_concerns: Vec<(ValidationAlgorithm, f64)>,
252 pub statistical_significance: f64,
254}
255
256pub struct AdvancedNumericalValidator {
258 config: ValidationConfig,
260 test_cases: Vec<ValidationTestCase>,
262 tolerances: ValidationTolerances,
264 results: Vec<ValidationResult>,
266}
267
268#[derive(Debug, Clone)]
270pub struct ValidationConfig {
271 pub verbose_logging: bool,
273 pub benchmark_performance: bool,
275 pub statistical_analysis: bool,
277 pub warmup_runs: usize,
279 pub cross_validation: bool,
281 pub random_seed: Option<u64>,
283}
284
285impl Default for ValidationConfig {
286 fn default() -> Self {
287 Self {
288 verbose_logging: true,
289 benchmark_performance: true,
290 statistical_analysis: true,
291 warmup_runs: 3,
292 cross_validation: true,
293 random_seed: Some(42),
294 }
295 }
296}
297
298impl AdvancedNumericalValidator {
299 pub fn new(config: ValidationConfig) -> Self {
301 Self {
302 config,
303 test_cases: Vec::new(),
304 tolerances: ValidationTolerances::default(),
305 results: Vec::new(),
306 }
307 }
308
309 pub fn add_test_case(&mut self, testcase: ValidationTestCase) {
311 self.test_cases.push(testcase);
312 }
313
314 pub fn set_tolerances(&mut self, tolerances: ValidationTolerances) {
316 self.tolerances = tolerances;
317 }
318
319 pub fn run_validation(&mut self) -> Result<ValidationReport> {
321 println!("š¬ Starting Advanced Numerical Accuracy Validation");
322 println!("==================================================");
323
324 let start_time = Instant::now();
325 self.results.clear();
326
327 if let Some(seed) = self.config.random_seed {
329 println!("š² Using random seed: {seed}");
331 }
332
333 for test_case in &self.test_cases.clone() {
335 println!("\nš Validating test case: {}", test_case.name);
336 println!("{}---{}", "".repeat(test_case.name.len()), "".repeat(20));
337
338 self.validate_test_case(test_case)?;
339 }
340
341 let total_time = start_time.elapsed();
342
343 let report = self.generate_validation_report(total_time)?;
345
346 println!("\nā
Validation completed in {total_time:?}");
347 self.print_validation_summary(&report.summary);
348
349 Ok(report)
350 }
351
352 fn validate_test_case(&mut self, testcase: &ValidationTestCase) -> Result<()> {
354 let graph = self.generate_test_graph(&testcase.graph_generator)?;
356
357 println!(
358 " š Generated graph: {} nodes, {} edges",
359 graph.node_count(),
360 graph.edge_count()
361 );
362
363 for algorithm in &testcase.algorithms {
365 println!(" š§® Validating algorithm: {algorithm:?}");
366
367 let mut run_results = Vec::new();
369
370 for run in 0..testcase.num_runs {
371 if self.config.verbose_logging && testcase.num_runs > 1 {
372 println!(" š Run {} of {}", run + 1, testcase.num_runs);
373 }
374
375 let result = self.validate_algorithm(&graph, algorithm, &testcase.tolerances)?;
376 run_results.push(result);
377 }
378
379 let aggregated_result = self.aggregate_validation_results(run_results, testcase)?;
381
382 println!(
383 " ā
Result: {} (accuracy: {:.4}, speedup: {:.2}x)",
384 if aggregated_result.passed {
385 "PASS"
386 } else {
387 "FAIL"
388 },
389 aggregated_result.accuracy_score,
390 aggregated_result.speedup_factor
391 );
392
393 if let Some(ref error) = aggregated_result.error_message {
394 println!(" ā Error: {error}");
395 }
396
397 self.results.push(aggregated_result);
398 }
399
400 Ok(())
401 }
402
403 fn validate_algorithm(
405 &self,
406 graph: &Graph<usize, f64>,
407 algorithm: &ValidationAlgorithm,
408 tolerances: &ValidationTolerances,
409 ) -> Result<ValidationResult> {
410 let (standard_result, standard_time) = self.run_standard_algorithm(graph, algorithm)?;
412
413 let (advanced_result, advanced_time) = self.run_advanced_algorithm(graph, algorithm)?;
415
416 let metrics = self.compare_results(&standard_result, &advanced_result, algorithm)?;
418
419 let passed = self.evaluate_validation_pass(&metrics, tolerances);
421
422 let accuracy_score = self.calculate_accuracy_score(&metrics);
424
425 let speedup_factor = standard_time.as_secs_f64() / advanced_time.as_secs_f64();
427
428 let error_message = if !passed {
429 Some(format!(
430 "Validation failed: accuracy score {accuracy_score:.6} below threshold"
431 ))
432 } else {
433 None
434 };
435
436 Ok(ValidationResult {
437 algorithm: algorithm.clone(),
438 test_case: "current".to_string(), passed,
440 accuracy_score,
441 standard_time,
442 advanced_time,
443 speedup_factor,
444 metrics,
445 error_message,
446 })
447 }
448
449 fn run_standard_algorithm(
451 &self,
452 graph: &Graph<usize, f64>,
453 algorithm: &ValidationAlgorithm,
454 ) -> Result<(AlgorithmOutput, Duration)> {
455 let start = Instant::now();
456
457 let result = match algorithm {
458 ValidationAlgorithm::ConnectedComponents => {
459 let components = connected_components(graph);
460 let mut component_map = HashMap::new();
461 for (component_id, component) in components.iter().enumerate() {
462 for node in component {
463 component_map.insert(*node, component_id);
464 }
465 }
466 AlgorithmOutput::ComponentMap(component_map)
467 }
468 ValidationAlgorithm::StronglyConnectedComponents => {
469 let components = connected_components(graph);
471 let mut component_map = HashMap::new();
472 for (component_id, component) in components.iter().enumerate() {
473 for node in component {
474 component_map.insert(*node, component_id);
475 }
476 }
477 AlgorithmOutput::ComponentMap(component_map)
478 }
479 ValidationAlgorithm::PageRank {
480 damping,
481 max_iterations: _,
482 tolerance,
483 } => AlgorithmOutput::ScoreMap(pagerank_centrality(graph, *damping, *tolerance)?),
484 ValidationAlgorithm::BetweennessCentrality => {
485 AlgorithmOutput::ScoreMap(betweenness_centrality(graph, false))
486 }
487 ValidationAlgorithm::ClosenessCentrality => {
488 AlgorithmOutput::ScoreMap(closeness_centrality(graph, false))
489 }
490 ValidationAlgorithm::DegreeCentrality => AlgorithmOutput::ScoreMap({
491 let mut degree_map = HashMap::new();
492 for node in graph.nodes() {
493 degree_map.insert(*node, graph.degree(node) as f64);
494 }
495 degree_map
496 }),
497 ValidationAlgorithm::ShortestPaths { source } => {
498 use petgraph::algo::dijkstra;
500
501 let graph_ref = graph.inner();
502 let source_idx = graph_ref
503 .node_indices()
504 .find(|&idx| &graph_ref[idx] == source)
505 .ok_or_else(|| GraphError::node_not_found("source node"))?;
506
507 let distances = dijkstra(graph_ref, source_idx, None, |e| *e.weight());
508 let mut distance_map = HashMap::new();
509 for (node_idx, distance) in distances {
510 distance_map.insert(graph_ref[node_idx], distance);
511 }
512 AlgorithmOutput::DistanceMap(distance_map)
513 }
514 ValidationAlgorithm::AllPairsShortestPaths => {
515 let distance_matrix = floyd_warshall(graph)?;
516 let mut distance_map = HashMap::new();
517
518 for i in 0..distance_matrix.nrows() {
519 for j in 0..distance_matrix.ncols() {
520 distance_map.insert((i, j), distance_matrix[[i, j]]);
521 }
522 }
523
524 AlgorithmOutput::AllPairsDistances(distance_map)
525 }
526 ValidationAlgorithm::LouvainCommunities => {
527 let result = louvain_communities_result(graph);
528 AlgorithmOutput::ComponentMap(result.node_communities)
529 }
530 ValidationAlgorithm::LabelPropagation { max_iterations } => {
531 let result = label_propagation_result(graph, *max_iterations);
532 AlgorithmOutput::ComponentMap(result.node_communities)
533 }
534 };
535
536 let elapsed = start.elapsed();
537 Ok((result, elapsed))
538 }
539
540 fn run_advanced_algorithm(
542 &self,
543 graph: &Graph<usize, f64>,
544 algorithm: &ValidationAlgorithm,
545 ) -> Result<(AlgorithmOutput, Duration)> {
546 let mut processor = create_enhanced_advanced_processor();
547 let start = Instant::now();
548
549 let result = match algorithm {
550 ValidationAlgorithm::ConnectedComponents => {
551 let components =
552 execute_with_enhanced_advanced(graph, |g| Ok(connected_components(g)))?;
553 AlgorithmOutput::ComponentMap({
554 let mut component_map = HashMap::new();
555 for (component_id, component) in components.iter().enumerate() {
556 for node in component {
557 component_map.insert(*node, component_id);
558 }
559 }
560 component_map
561 })
562 }
563 ValidationAlgorithm::StronglyConnectedComponents => {
564 let components = execute_with_enhanced_advanced(graph, |g| {
565 Ok(vec![g.nodes().into_iter().cloned().collect::<HashSet<_>>()])
567 })?;
568 AlgorithmOutput::ComponentMap({
569 let mut component_map = HashMap::new();
570 for (component_id, component) in components.iter().enumerate() {
571 for node in component {
572 component_map.insert(*node, component_id);
573 }
574 }
575 component_map
576 })
577 }
578 ValidationAlgorithm::PageRank {
579 damping,
580 max_iterations: _,
581 tolerance,
582 } => {
583 let scores = execute_with_enhanced_advanced(graph, |g| {
584 pagerank_centrality(g, *damping, *tolerance)
585 })?;
586 AlgorithmOutput::ScoreMap(scores)
587 }
588 ValidationAlgorithm::BetweennessCentrality => {
589 let scores = execute_with_enhanced_advanced(graph, |g| {
590 Ok(betweenness_centrality(g, false))
591 })?;
592 AlgorithmOutput::ScoreMap(scores)
593 }
594 ValidationAlgorithm::ClosenessCentrality => {
595 let scores =
596 execute_with_enhanced_advanced(graph, |g| Ok(closeness_centrality(g, false)))?;
597 AlgorithmOutput::ScoreMap(scores)
598 }
599 ValidationAlgorithm::DegreeCentrality => {
600 let scores = execute_with_enhanced_advanced(graph, |g| {
601 let mut degree_map = HashMap::new();
602 for node in g.nodes() {
603 degree_map.insert(*node, g.degree(node) as f64);
604 }
605 Ok(degree_map)
606 })?;
607 AlgorithmOutput::ScoreMap(scores)
608 }
609 ValidationAlgorithm::ShortestPaths { source } => {
610 let distances = execute_with_enhanced_advanced(graph, |g| {
611 use petgraph::algo::dijkstra;
612 let graph_ref = g.inner();
613 let source_idx = graph_ref
614 .node_indices()
615 .find(|&idx| &graph_ref[idx] == source)
616 .ok_or_else(|| crate::error::GraphError::node_not_found("source node"))?;
617
618 let distances = dijkstra(graph_ref, source_idx, None, |e| *e.weight());
619 let mut distance_map = HashMap::new();
620 for (node_idx, distance) in distances {
621 distance_map.insert(graph_ref[node_idx], distance);
622 }
623 Ok(distance_map)
624 })?;
625 AlgorithmOutput::DistanceMap(distances)
626 }
627 ValidationAlgorithm::AllPairsShortestPaths => {
628 let distances = execute_with_enhanced_advanced(graph, |g| {
629 let distance_matrix = floyd_warshall(g)?;
630 let mut distance_map = HashMap::new();
631
632 for i in 0..distance_matrix.nrows() {
633 for j in 0..distance_matrix.ncols() {
634 distance_map.insert((i, j), distance_matrix[[i, j]]);
635 }
636 }
637
638 Ok(distance_map)
639 })?;
640 AlgorithmOutput::AllPairsDistances(distances)
641 }
642 ValidationAlgorithm::LouvainCommunities => {
643 let communities = execute_with_enhanced_advanced(graph, |g| {
644 Ok(louvain_communities_result(g).node_communities)
645 })?;
646 AlgorithmOutput::ComponentMap(communities)
647 }
648 ValidationAlgorithm::LabelPropagation { max_iterations } => {
649 let communities = execute_with_enhanced_advanced(graph, |g| {
650 Ok(label_propagation_result(g, *max_iterations).node_communities)
651 })?;
652 AlgorithmOutput::ComponentMap(communities)
653 }
654 };
655
656 let elapsed = start.elapsed();
657 Ok((result, elapsed))
658 }
659
660 fn generate_test_graph(&self, generator: &GraphGenerator) -> Result<Graph<usize, f64>> {
662 match generator {
663 GraphGenerator::Random {
664 nodes,
665 edges,
666 directed: _,
667 } => erdos_renyi_graph(
668 *nodes,
669 *edges as f64 / (*nodes * (*nodes - 1) / 2) as f64,
670 &mut scirs2_core::random::rng(),
671 ),
672 GraphGenerator::ErdosRenyi { nodes, probability } => {
673 erdos_renyi_graph(*nodes, *probability, &mut scirs2_core::random::rng())
674 }
675 GraphGenerator::BarabasiAlbert {
676 nodes,
677 edges_per_node,
678 } => barabasi_albert_graph(*nodes, *edges_per_node, &mut scirs2_core::random::rng()),
679 GraphGenerator::SmallWorld { nodes, k: _, p: _ } => {
680 erdos_renyi_graph(*nodes, 6.0 / *nodes as f64, &mut scirs2_core::random::rng())
683 }
684 GraphGenerator::Complete { nodes } => {
685 let mut graph = Graph::new();
686 for i in 0..*nodes {
687 graph.add_node(i);
688 }
689 for i in 0..*nodes {
690 for j in (i + 1)..*nodes {
691 graph.add_edge(i, j, 1.0).unwrap();
692 }
693 }
694 Ok(graph)
695 }
696 GraphGenerator::Custom { generator } => generator(),
697 }
698 }
699
700 fn compare_results(
702 &self,
703 standard: &AlgorithmOutput,
704 advanced: &AlgorithmOutput,
705 algorithm: &ValidationAlgorithm,
706 ) -> Result<ValidationMetrics> {
707 match (standard, advanced) {
708 (AlgorithmOutput::ScoreMap(std_scores), AlgorithmOutput::ScoreMap(ut_scores)) => {
709 self.compare_score_maps(std_scores, ut_scores)
710 }
711 (AlgorithmOutput::ComponentMap(std_comps), AlgorithmOutput::ComponentMap(ut_comps)) => {
712 self.compare_component_maps(std_comps, ut_comps)
713 }
714 (AlgorithmOutput::DistanceMap(std_dists), AlgorithmOutput::DistanceMap(ut_dists)) => {
715 self.compare_distance_maps(std_dists, ut_dists)
716 }
717 (
718 AlgorithmOutput::AllPairsDistances(std_all),
719 AlgorithmOutput::AllPairsDistances(ut_all),
720 ) => self.compare_all_pairs_distances(std_all, ut_all),
721 _ => Err(crate::error::GraphError::InvalidParameter {
722 param: "algorithm_outputs".to_string(),
723 value: "mismatched types".to_string(),
724 expected: "matching output types".to_string(),
725 context: "Mismatched algorithm output types".to_string(),
726 }),
727 }
728 }
729
730 fn compare_score_maps(
732 &self,
733 standard: &HashMap<usize, f64>,
734 advanced: &HashMap<usize, f64>,
735 ) -> Result<ValidationMetrics> {
736 let mut absolute_errors = Vec::new();
737 let mut standard_values = Vec::new();
738 let mut advanced_values = Vec::new();
739 let mut exact_matches = 0;
740
741 let common_keys: Vec<_> = standard
743 .keys()
744 .filter(|k| advanced.contains_key(k))
745 .collect();
746
747 for &key in &common_keys {
748 let std_val = standard[key];
749 let ut_val = advanced[key];
750
751 let abs_error = (std_val - ut_val).abs();
752 absolute_errors.push(abs_error);
753 standard_values.push(std_val);
754 advanced_values.push(ut_val);
755
756 if abs_error < self.tolerances.absolute_tolerance {
757 exact_matches += 1;
758 }
759 }
760
761 let max_absolute_error = absolute_errors.iter().fold(0.0f64, |a, &b| a.max(b));
762 let mean_absolute_error =
763 absolute_errors.iter().sum::<f64>() / absolute_errors.len() as f64;
764 let root_mean_square_error = (absolute_errors.iter().map(|e| e * e).sum::<f64>()
765 / absolute_errors.len() as f64)
766 .sqrt();
767
768 let pearson_correlation =
770 self.calculate_pearson_correlation(&standard_values, &advanced_values);
771 let spearman_correlation =
772 self.calculate_spearman_correlation(&standard_values, &advanced_values);
773
774 Ok(ValidationMetrics {
775 max_absolute_error,
776 mean_absolute_error,
777 root_mean_square_error,
778 pearson_correlation,
779 spearman_correlation,
780 elements_compared: common_keys.len(),
781 exact_matches,
782 custom_metrics: HashMap::new(),
783 })
784 }
785
786 fn compare_component_maps(
788 &self,
789 standard: &HashMap<usize, usize>,
790 advanced: &HashMap<usize, usize>,
791 ) -> Result<ValidationMetrics> {
792 let mut exact_matches = 0;
793 let common_keys: Vec<_> = standard
794 .keys()
795 .filter(|k| advanced.contains_key(k))
796 .collect();
797
798 let normalized_std = self.normalize_component_map(standard);
801 let normalized_ut = self.normalize_component_map(advanced);
802
803 for &key in &common_keys {
804 if normalized_std.get(key) == normalized_ut.get(key) {
805 exact_matches += 1;
806 }
807 }
808
809 let partition_similarity = exact_matches as f64 / common_keys.len() as f64;
811
812 Ok(ValidationMetrics {
813 max_absolute_error: if exact_matches == common_keys.len() {
814 0.0
815 } else {
816 1.0
817 },
818 mean_absolute_error: 1.0 - partition_similarity,
819 root_mean_square_error: (1.0 - partition_similarity).sqrt(),
820 pearson_correlation: partition_similarity,
821 spearman_correlation: partition_similarity,
822 elements_compared: common_keys.len(),
823 exact_matches,
824 custom_metrics: {
825 let mut metrics = HashMap::new();
826 metrics.insert("partition_similarity".to_string(), partition_similarity);
827 metrics
828 },
829 })
830 }
831
832 fn compare_distance_maps(
834 &self,
835 standard: &HashMap<usize, f64>,
836 advanced: &HashMap<usize, f64>,
837 ) -> Result<ValidationMetrics> {
838 self.compare_score_maps(standard, advanced)
840 }
841
842 fn compare_all_pairs_distances(
844 &self,
845 standard: &HashMap<(usize, usize), f64>,
846 advanced: &HashMap<(usize, usize), f64>,
847 ) -> Result<ValidationMetrics> {
848 let mut absolute_errors = Vec::new();
849 let mut exact_matches = 0;
850
851 let common_keys: Vec<_> = standard
852 .keys()
853 .filter(|k| advanced.contains_key(k))
854 .collect();
855
856 for &key in &common_keys {
857 let std_val = standard[key];
858 let ut_val = advanced[key];
859
860 let abs_error = (std_val - ut_val).abs();
861 absolute_errors.push(abs_error);
862
863 if abs_error < self.tolerances.absolute_tolerance {
864 exact_matches += 1;
865 }
866 }
867
868 let max_absolute_error = absolute_errors.iter().fold(0.0f64, |a, &b| a.max(b));
869 let mean_absolute_error =
870 absolute_errors.iter().sum::<f64>() / absolute_errors.len() as f64;
871 let root_mean_square_error = (absolute_errors.iter().map(|e| e * e).sum::<f64>()
872 / absolute_errors.len() as f64)
873 .sqrt();
874
875 Ok(ValidationMetrics {
876 max_absolute_error,
877 mean_absolute_error,
878 root_mean_square_error,
879 pearson_correlation: 1.0 - mean_absolute_error, spearman_correlation: 1.0 - mean_absolute_error, elements_compared: common_keys.len(),
882 exact_matches,
883 custom_metrics: HashMap::new(),
884 })
885 }
886
887 fn normalize_component_map(&self, components: &HashMap<usize, usize>) -> HashMap<usize, usize> {
889 let mut normalized = HashMap::new();
890 let mut component_map = HashMap::new();
891 let mut next_id = 0;
892
893 for (&node, &component) in components {
894 let normalized_component = *component_map.entry(component).or_insert_with(|| {
895 let id = next_id;
896 next_id += 1;
897 id
898 });
899 normalized.insert(node, normalized_component);
900 }
901
902 normalized
903 }
904
905 fn calculate_pearson_correlation(&self, x: &[f64], y: &[f64]) -> f64 {
907 if x.len() != y.len() || x.is_empty() {
908 return 0.0;
909 }
910
911 let n = x.len() as f64;
912 let sum_x: f64 = x.iter().sum();
913 let sum_y: f64 = y.iter().sum();
914 let sum_x2: f64 = x.iter().map(|v| v * v).sum();
915 let sum_y2: f64 = y.iter().map(|v| v * v).sum();
916 let sum_xy: f64 = x.iter().zip(y).map(|(a, b)| a * b).sum();
917
918 let numerator = n * sum_xy - sum_x * sum_y;
919 let denominator = ((n * sum_x2 - sum_x * sum_x) * (n * sum_y2 - sum_y * sum_y)).sqrt();
920
921 if denominator == 0.0 {
922 0.0
923 } else {
924 numerator / denominator
925 }
926 }
927
928 fn calculate_spearman_correlation(&self, x: &[f64], y: &[f64]) -> f64 {
930 if x.len() != y.len() || x.is_empty() {
931 return 0.0;
932 }
933
934 let rank_x = self.calculate_ranks(x);
936 let rank_y = self.calculate_ranks(y);
937
938 self.calculate_pearson_correlation(&rank_x, &rank_y)
940 }
941
942 fn calculate_ranks(&self, values: &[f64]) -> Vec<f64> {
944 let mut indexed_values: Vec<(usize, f64)> =
945 values.iter().enumerate().map(|(i, &v)| (i, v)).collect();
946
947 indexed_values.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
948
949 let mut ranks = vec![0.0; values.len()];
950 for (rank, (original_index, _)) in indexed_values.iter().enumerate() {
951 ranks[*original_index] = (rank + 1) as f64;
952 }
953
954 ranks
955 }
956
957 fn evaluate_validation_pass(
959 &self,
960 metrics: &ValidationMetrics,
961 tolerances: &ValidationTolerances,
962 ) -> bool {
963 metrics.max_absolute_error <= tolerances.absolute_tolerance
964 && metrics.pearson_correlation >= tolerances.correlation_threshold
965 && metrics.mean_absolute_error <= tolerances.centrality_deviation_threshold
966 }
967
968 fn calculate_accuracy_score(&self, metrics: &ValidationMetrics) -> f64 {
970 let correlation_weight = 0.4;
972 let error_weight = 0.3;
973 let exact_match_weight = 0.3;
974
975 let correlation_score = (metrics.pearson_correlation + metrics.spearman_correlation) / 2.0;
976 let error_score = 1.0 - (metrics.mean_absolute_error / (1.0 + metrics.mean_absolute_error));
977 let exact_match_score = metrics.exact_matches as f64 / metrics.elements_compared as f64;
978
979 correlation_weight * correlation_score
980 + error_weight * error_score
981 + exact_match_weight * exact_match_score
982 }
983
984 fn aggregate_validation_results(
986 &self,
987 results: Vec<ValidationResult>,
988 test_case: &ValidationTestCase,
989 ) -> Result<ValidationResult> {
990 if results.is_empty() {
991 return Err(crate::error::GraphError::InvalidParameter {
992 param: "results".to_string(),
993 value: "empty".to_string(),
994 expected: "non-empty vector".to_string(),
995 context: "No validation results to aggregate".to_string(),
996 });
997 }
998
999 let passed = results.iter().all(|r| r.passed);
1000 let accuracy_score =
1001 results.iter().map(|r| r.accuracy_score).sum::<f64>() / results.len() as f64;
1002 let speedup_factor =
1003 results.iter().map(|r| r.speedup_factor).sum::<f64>() / results.len() as f64;
1004
1005 let standard_time = Duration::from_secs_f64(
1006 results
1007 .iter()
1008 .map(|r| r.standard_time.as_secs_f64())
1009 .sum::<f64>()
1010 / results.len() as f64,
1011 );
1012 let advanced_time = Duration::from_secs_f64(
1013 results
1014 .iter()
1015 .map(|r| r.advanced_time.as_secs_f64())
1016 .sum::<f64>()
1017 / results.len() as f64,
1018 );
1019
1020 let metrics = ValidationMetrics {
1022 max_absolute_error: results
1023 .iter()
1024 .map(|r| r.metrics.max_absolute_error)
1025 .fold(0.0, f64::max),
1026 mean_absolute_error: results
1027 .iter()
1028 .map(|r| r.metrics.mean_absolute_error)
1029 .sum::<f64>()
1030 / results.len() as f64,
1031 root_mean_square_error: results
1032 .iter()
1033 .map(|r| r.metrics.root_mean_square_error)
1034 .sum::<f64>()
1035 / results.len() as f64,
1036 pearson_correlation: results
1037 .iter()
1038 .map(|r| r.metrics.pearson_correlation)
1039 .sum::<f64>()
1040 / results.len() as f64,
1041 spearman_correlation: results
1042 .iter()
1043 .map(|r| r.metrics.spearman_correlation)
1044 .sum::<f64>()
1045 / results.len() as f64,
1046 elements_compared: results
1047 .iter()
1048 .map(|r| r.metrics.elements_compared)
1049 .sum::<usize>()
1050 / results.len(),
1051 exact_matches: results
1052 .iter()
1053 .map(|r| r.metrics.exact_matches)
1054 .sum::<usize>()
1055 / results.len(),
1056 custom_metrics: HashMap::new(),
1057 };
1058
1059 let error_message = if !passed {
1060 Some(format!(
1061 "Aggregated validation failed: average accuracy {accuracy_score:.6}"
1062 ))
1063 } else {
1064 None
1065 };
1066
1067 Ok(ValidationResult {
1068 algorithm: results[0].algorithm.clone(),
1069 test_case: test_case.name.clone(),
1070 passed,
1071 accuracy_score,
1072 standard_time,
1073 advanced_time,
1074 speedup_factor,
1075 metrics,
1076 error_message,
1077 })
1078 }
1079
1080 fn generate_validation_report(&self, totaltime: Duration) -> Result<ValidationReport> {
1082 let summary = self.generate_validation_summary(totaltime);
1083 let performance_analysis = self.generate_performance_analysis();
1084 let accuracy_analysis = self.generate_accuracy_analysis();
1085 let recommendations = self.generate_recommendations();
1086
1087 Ok(ValidationReport {
1088 summary,
1089 test_results: self.results.clone(),
1090 performance_analysis,
1091 accuracy_analysis,
1092 recommendations,
1093 timestamp: SystemTime::now(),
1094 })
1095 }
1096
1097 fn generate_validation_summary(&self, totaltime: Duration) -> ValidationSummary {
1099 let total_tests = self.results.len();
1100 let tests_passed = self.results.iter().filter(|r| r.passed).count();
1101 let pass_rate = tests_passed as f64 / total_tests as f64;
1102
1103 let average_accuracy =
1104 self.results.iter().map(|r| r.accuracy_score).sum::<f64>() / total_tests as f64;
1105
1106 let average_speedup =
1107 self.results.iter().map(|r| r.speedup_factor).sum::<f64>() / total_tests as f64;
1108
1109 ValidationSummary {
1110 total_tests,
1111 tests_passed,
1112 pass_rate,
1113 average_accuracy,
1114 average_speedup,
1115 total_time: totaltime,
1116 }
1117 }
1118
1119 fn generate_performance_analysis(&self) -> PerformanceAnalysis {
1121 let speedups: Vec<f64> = self.results.iter().map(|r| r.speedup_factor).collect();
1122 let best_speedup = speedups.iter().fold(0.0f64, |a, &b| a.max(b));
1123 let worst_speedup = speedups.iter().fold(f64::INFINITY, |a, &b| a.min(b));
1124
1125 let mut algorithm_speedups: HashMap<ValidationAlgorithm, Vec<f64>> = HashMap::new();
1126 for result in &self.results {
1127 algorithm_speedups
1128 .entry(result.algorithm.clone())
1129 .or_default()
1130 .push(result.speedup_factor);
1131 }
1132
1133 let mut top_performers = Vec::new();
1134 let mut performance_regressions = Vec::new();
1135
1136 for (algorithm, speedups) in algorithm_speedups {
1137 let avg_speedup = speedups.iter().sum::<f64>() / speedups.len() as f64;
1138 if avg_speedup >= 1.5 {
1139 top_performers.push((algorithm.clone(), avg_speedup));
1140 }
1141 if avg_speedup < 1.0 {
1142 performance_regressions.push((algorithm, avg_speedup));
1143 }
1144 }
1145
1146 top_performers.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
1147 performance_regressions.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
1148
1149 PerformanceAnalysis {
1150 best_speedup,
1151 worst_speedup,
1152 top_performers,
1153 performance_regressions,
1154 memory_efficiency: 1.0, }
1156 }
1157
1158 fn generate_accuracy_analysis(&self) -> AccuracyAnalysis {
1160 let accuracies: Vec<f64> = self.results.iter().map(|r| r.accuracy_score).collect();
1161 let best_accuracy = accuracies.iter().fold(0.0f64, |a, &b| a.max(b));
1162 let worst_accuracy = accuracies.iter().fold(1.0f64, |a, &b| a.min(b));
1163
1164 let perfect_accuracy_algorithms = self
1165 .results
1166 .iter()
1167 .filter(|r| r.accuracy_score >= 0.999)
1168 .map(|r| r.algorithm.clone())
1169 .collect();
1170
1171 let accuracy_concerns = self
1172 .results
1173 .iter()
1174 .filter(|r| r.accuracy_score < 0.95)
1175 .map(|r| (r.algorithm.clone(), r.accuracy_score))
1176 .collect();
1177
1178 AccuracyAnalysis {
1179 best_accuracy,
1180 worst_accuracy,
1181 perfect_accuracy_algorithms,
1182 accuracy_concerns,
1183 statistical_significance: 0.95, }
1185 }
1186
1187 fn generate_recommendations(&self) -> Vec<String> {
1189 let mut recommendations = Vec::new();
1190
1191 let failed_tests = self.results.iter().filter(|r| !r.passed).count();
1192 if failed_tests > 0 {
1193 recommendations.push(format!(
1194 "Address {failed_tests} failed validation tests to improve overall accuracy"
1195 ));
1196 }
1197
1198 let low_accuracy_tests = self
1199 .results
1200 .iter()
1201 .filter(|r| r.accuracy_score < 0.95)
1202 .count();
1203 if low_accuracy_tests > 0 {
1204 recommendations.push(format!(
1205 "Investigate {low_accuracy_tests} tests with accuracy scores below 0.95"
1206 ));
1207 }
1208
1209 let slow_algorithms = self
1210 .results
1211 .iter()
1212 .filter(|r| r.speedup_factor < 1.0)
1213 .count();
1214 if slow_algorithms > 0 {
1215 recommendations.push(format!(
1216 "Optimize {slow_algorithms} algorithms showing performance regressions"
1217 ));
1218 }
1219
1220 let avg_accuracy =
1221 self.results.iter().map(|r| r.accuracy_score).sum::<f64>() / self.results.len() as f64;
1222 if avg_accuracy < 0.98 {
1223 recommendations.push(
1224 "Consider tightening numerical precision in advanced optimizations".to_string(),
1225 );
1226 }
1227
1228 let avg_speedup =
1229 self.results.iter().map(|r| r.speedup_factor).sum::<f64>() / self.results.len() as f64;
1230 if avg_speedup < 1.5 {
1231 recommendations.push(
1232 "Investigate opportunities for additional performance optimizations".to_string(),
1233 );
1234 }
1235
1236 if recommendations.is_empty() {
1237 recommendations.push("All validation tests passed successfully. Consider adding more comprehensive test cases.".to_string());
1238 }
1239
1240 recommendations
1241 }
1242
1243 fn print_validation_summary(&self, summary: &ValidationSummary) {
1245 println!("\nš Validation Summary");
1246 println!("===================");
1247 println!("Total tests: {}", summary.total_tests);
1248 println!(
1249 "Tests passed: {} ({:.1}%)",
1250 summary.tests_passed,
1251 summary.pass_rate * 100.0
1252 );
1253 println!("Average accuracy: {:.4}", summary.average_accuracy);
1254 println!("Average speedup: {:.2}x", summary.average_speedup);
1255 println!("Total validation time: {:?}", summary.total_time);
1256
1257 if summary.pass_rate >= 0.95 {
1258 println!("ā
Validation PASSED: Advanced mode maintains high numerical accuracy");
1259 } else {
1260 println!("ā Validation FAILED: Accuracy issues detected");
1261 }
1262 }
1263}
1264
1265#[derive(Debug, Clone)]
1267pub enum AlgorithmOutput {
1268 ScoreMap(HashMap<usize, f64>),
1270 ComponentMap(HashMap<usize, usize>),
1272 DistanceMap(HashMap<usize, f64>),
1274 AllPairsDistances(HashMap<(usize, usize), f64>),
1276}
1277
1278#[allow(dead_code)]
1280pub fn create_comprehensive_validation_suite() -> AdvancedNumericalValidator {
1281 let mut validator = AdvancedNumericalValidator::new(ValidationConfig::default());
1282
1283 validator.add_test_case(ValidationTestCase {
1285 name: "Small Random Graphs".to_string(),
1286 graph_generator: GraphGenerator::Random {
1287 nodes: 100,
1288 edges: 200,
1289 directed: false,
1290 },
1291 algorithms: vec![
1292 ValidationAlgorithm::ConnectedComponents,
1293 ValidationAlgorithm::PageRank {
1294 damping: 0.85,
1295 max_iterations: 100,
1296 tolerance: 1e-6,
1297 },
1298 ValidationAlgorithm::BetweennessCentrality,
1299 ValidationAlgorithm::ShortestPaths { source: 0 },
1300 ],
1301 tolerances: ValidationTolerances::default(),
1302 num_runs: 5,
1303 });
1304
1305 validator.add_test_case(ValidationTestCase {
1307 name: "Scale-Free Networks".to_string(),
1308 graph_generator: GraphGenerator::BarabasiAlbert {
1309 nodes: 500,
1310 edges_per_node: 3,
1311 },
1312 algorithms: vec![
1313 ValidationAlgorithm::ConnectedComponents,
1314 ValidationAlgorithm::PageRank {
1315 damping: 0.85,
1316 max_iterations: 100,
1317 tolerance: 1e-6,
1318 },
1319 ValidationAlgorithm::LouvainCommunities,
1320 ValidationAlgorithm::ClosenessCentrality,
1321 ],
1322 tolerances: ValidationTolerances::default(),
1323 num_runs: 3,
1324 });
1325
1326 validator.add_test_case(ValidationTestCase {
1328 name: "Dense Random Networks".to_string(),
1329 graph_generator: GraphGenerator::ErdosRenyi {
1330 nodes: 200,
1331 probability: 0.1,
1332 },
1333 algorithms: vec![
1334 ValidationAlgorithm::AllPairsShortestPaths,
1335 ValidationAlgorithm::DegreeCentrality,
1336 ValidationAlgorithm::LabelPropagation { max_iterations: 50 },
1337 ],
1338 tolerances: ValidationTolerances::default(),
1339 num_runs: 3,
1340 });
1341
1342 validator.add_test_case(ValidationTestCase {
1344 name: "Sparse Large Graphs".to_string(),
1345 graph_generator: GraphGenerator::Random {
1346 nodes: 2000,
1347 edges: 4000,
1348 directed: false,
1349 },
1350 algorithms: vec![
1351 ValidationAlgorithm::ConnectedComponents,
1352 ValidationAlgorithm::PageRank {
1353 damping: 0.85,
1354 max_iterations: 50,
1355 tolerance: 1e-5,
1356 },
1357 ValidationAlgorithm::LouvainCommunities,
1358 ],
1359 tolerances: ValidationTolerances {
1360 absolute_tolerance: 1e-5,
1361 relative_tolerance: 1e-4,
1362 correlation_threshold: 0.9,
1363 ..ValidationTolerances::default()
1364 },
1365 num_runs: 2,
1366 });
1367
1368 validator
1369}
1370
1371#[allow(dead_code)]
1373pub fn run_quick_validation() -> Result<ValidationReport> {
1374 println!("š Running Quick Advanced Numerical Validation");
1375 println!("===============================================");
1376
1377 let mut validator = AdvancedNumericalValidator::new(ValidationConfig {
1378 verbose_logging: true,
1379 warmup_runs: 1,
1380 ..ValidationConfig::default()
1381 });
1382
1383 validator.add_test_case(ValidationTestCase {
1385 name: "Quick Validation".to_string(),
1386 graph_generator: GraphGenerator::Random {
1387 nodes: 50,
1388 edges: 100,
1389 directed: false,
1390 },
1391 algorithms: vec![
1392 ValidationAlgorithm::ConnectedComponents,
1393 ValidationAlgorithm::PageRank {
1394 damping: 0.85,
1395 max_iterations: 20,
1396 tolerance: 1e-4,
1397 },
1398 ],
1399 tolerances: ValidationTolerances::default(),
1400 num_runs: 1,
1401 });
1402
1403 validator.run_validation()
1404}
1405
1406#[cfg(test)]
1407mod tests {
1408 use super::*;
1409
1410 #[test]
1411 fn test_validation_tolerances_default() {
1412 let tolerances = ValidationTolerances::default();
1413 assert_eq!(tolerances.absolute_tolerance, 1e-6);
1414 assert_eq!(tolerances.relative_tolerance, 1e-5);
1415 assert_eq!(tolerances.correlation_threshold, 0.95);
1416 }
1417
1418 #[test]
1419 fn test_graph_generation() {
1420 let validator = AdvancedNumericalValidator::new(ValidationConfig::default());
1421
1422 let graph = validator
1424 .generate_test_graph(&GraphGenerator::Random {
1425 nodes: 10,
1426 edges: 15,
1427 directed: false,
1428 })
1429 .unwrap();
1430 assert_eq!(graph.node_count(), 10);
1431 assert!(graph.edge_count() > 0 && graph.edge_count() <= 45); let complete = validator
1436 .generate_test_graph(&GraphGenerator::Complete { nodes: 5 })
1437 .unwrap();
1438 assert_eq!(complete.node_count(), 5);
1439 assert_eq!(complete.edge_count(), 10); }
1441
1442 #[test]
1443 fn test_pearson_correlation() {
1444 let validator = AdvancedNumericalValidator::new(ValidationConfig::default());
1445
1446 let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
1448 let y = vec![2.0, 4.0, 6.0, 8.0, 10.0];
1449 let corr = validator.calculate_pearson_correlation(&x, &y);
1450 assert!((corr - 1.0).abs() < 1e-10);
1451
1452 let y_neg = vec![10.0, 8.0, 6.0, 4.0, 2.0];
1454 let corr_neg = validator.calculate_pearson_correlation(&x, &y_neg);
1455 assert!((corr_neg + 1.0).abs() < 1e-10);
1456 }
1457
1458 #[test]
1459 fn test_component_map_normalization() {
1460 let validator = AdvancedNumericalValidator::new(ValidationConfig::default());
1461
1462 let mut components = HashMap::new();
1463 components.insert(0, 100);
1464 components.insert(1, 100);
1465 components.insert(2, 200);
1466 components.insert(3, 200);
1467
1468 let normalized = validator.normalize_component_map(&components);
1469
1470 assert_eq!(normalized[&0], normalized[&1]);
1472 assert_eq!(normalized[&2], normalized[&3]);
1473 assert_ne!(normalized[&0], normalized[&2]);
1474 }
1475
1476 #[test]
1477 fn test_quick_validation() {
1478 let result = run_quick_validation();
1480 assert!(result.is_ok());
1481
1482 let report = result.unwrap();
1483 assert!(report.summary.total_tests > 0);
1484 assert!(report.summary.pass_rate >= 0.0);
1485 assert!(report.summary.pass_rate <= 1.0);
1486 }
1487}