1use amari_core::Multivector;
55use std::collections::HashMap;
56
57pub mod error;
58pub mod tropical;
59
60#[cfg(feature = "formal-verification")]
62pub mod verified;
63#[cfg(feature = "formal-verification")]
64pub mod verified_contracts;
65
66pub use error::{NetworkError, NetworkResult};
67
68#[cfg(feature = "formal-verification")]
70pub use verified::{NetworkProperty, NetworkSignature, VerifiedGeometricNetwork};
71#[cfg(feature = "formal-verification")]
72pub use verified_contracts::{
73 GeometricProperties, GraphTheoreticProperties, TropicalProperties,
74 VerifiedContractGeometricNetwork,
75};
76
77#[derive(Clone, Debug)]
83pub struct GeometricNetwork<const P: usize, const Q: usize, const R: usize> {
84 nodes: Vec<Multivector<P, Q, R>>,
86 edges: Vec<GeometricEdge>,
88 node_metadata: HashMap<usize, NodeMetadata>,
90}
91
92#[derive(Clone, Debug, PartialEq)]
97pub struct GeometricEdge {
98 pub source: usize,
100 pub target: usize,
102 pub weight: f64,
104}
105
106#[derive(Clone, Debug, PartialEq)]
111#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
112pub struct NodeMetadata {
113 pub label: Option<String>,
115 pub properties: HashMap<String, f64>,
117}
118
119#[derive(Clone, Debug)]
124pub struct Community<const P: usize, const Q: usize, const R: usize> {
125 pub nodes: Vec<usize>,
127 pub geometric_centroid: Multivector<P, Q, R>,
129 pub cohesion_score: f64,
131}
132
133#[derive(Clone, Debug)]
138pub struct PropagationAnalysis {
139 pub coverage: Vec<usize>,
141 pub influence_scores: Vec<f64>,
143 pub convergence_time: usize,
145}
146
147impl NodeMetadata {
148 pub fn new() -> Self {
150 Self {
151 label: None,
152 properties: HashMap::new(),
153 }
154 }
155
156 pub fn with_label(label: impl Into<String>) -> Self {
158 Self {
159 label: Some(label.into()),
160 properties: HashMap::new(),
161 }
162 }
163
164 pub fn with_property(mut self, key: impl Into<String>, value: f64) -> Self {
166 self.properties.insert(key.into(), value);
167 self
168 }
169}
170
171impl Default for NodeMetadata {
172 fn default() -> Self {
173 Self::new()
174 }
175}
176
177impl<const P: usize, const Q: usize, const R: usize> GeometricNetwork<P, Q, R> {
178 pub fn new() -> Self {
180 Self {
181 nodes: Vec::new(),
182 edges: Vec::new(),
183 node_metadata: HashMap::new(),
184 }
185 }
186
187 pub fn with_capacity(num_nodes: usize, num_edges: usize) -> Self {
189 Self {
190 nodes: Vec::with_capacity(num_nodes),
191 edges: Vec::with_capacity(num_edges),
192 node_metadata: HashMap::with_capacity(num_nodes),
193 }
194 }
195
196 pub fn add_node(&mut self, position: Multivector<P, Q, R>) -> usize {
200 let index = self.nodes.len();
201 self.nodes.push(position);
202 index
203 }
204
205 pub fn add_node_with_metadata(
209 &mut self,
210 position: Multivector<P, Q, R>,
211 metadata: NodeMetadata,
212 ) -> usize {
213 let index = self.add_node(position);
214 self.node_metadata.insert(index, metadata);
215 index
216 }
217
218 pub fn add_edge(&mut self, source: usize, target: usize, weight: f64) -> NetworkResult<()> {
220 if source >= self.nodes.len() {
221 return Err(NetworkError::NodeIndexOutOfBounds(source));
222 }
223 if target >= self.nodes.len() {
224 return Err(NetworkError::NodeIndexOutOfBounds(target));
225 }
226
227 self.edges.push(GeometricEdge {
228 source,
229 target,
230 weight,
231 });
232 Ok(())
233 }
234
235 pub fn add_undirected_edge(&mut self, a: usize, b: usize, weight: f64) -> NetworkResult<()> {
237 self.add_edge(a, b, weight)?;
238 self.add_edge(b, a, weight)?;
239 Ok(())
240 }
241
242 pub fn num_nodes(&self) -> usize {
244 self.nodes.len()
245 }
246
247 pub fn num_edges(&self) -> usize {
249 self.edges.len()
250 }
251
252 pub fn get_node(&self, index: usize) -> Option<&Multivector<P, Q, R>> {
254 self.nodes.get(index)
255 }
256
257 pub fn get_metadata(&self, index: usize) -> Option<&NodeMetadata> {
259 self.node_metadata.get(&index)
260 }
261
262 pub fn neighbors(&self, node: usize) -> Vec<usize> {
264 self.edges
265 .iter()
266 .filter(|edge| edge.source == node)
267 .map(|edge| edge.target)
268 .collect()
269 }
270
271 pub fn degree(&self, node: usize) -> usize {
273 self.edges.iter().filter(|edge| edge.source == node).count()
274 }
275
276 pub fn edges(&self) -> &[GeometricEdge] {
278 &self.edges
279 }
280
281 pub fn geometric_distance(&self, node1: usize, node2: usize) -> NetworkResult<f64> {
286 if node1 >= self.nodes.len() {
287 return Err(NetworkError::NodeIndexOutOfBounds(node1));
288 }
289 if node2 >= self.nodes.len() {
290 return Err(NetworkError::NodeIndexOutOfBounds(node2));
291 }
292
293 let diff = self.nodes[node1].clone() - self.nodes[node2].clone();
294 Ok(diff.norm())
295 }
296
297 pub fn compute_geometric_centrality(&self) -> NetworkResult<Vec<f64>> {
303 if self.nodes.is_empty() {
304 return Ok(Vec::new());
305 }
306
307 let mut centrality = vec![0.0; self.nodes.len()];
308
309 for (i, centrality_value) in centrality.iter_mut().enumerate().take(self.nodes.len()) {
310 let mut total_distance = 0.0;
311 let mut reachable_count = 0;
312
313 for j in 0..self.nodes.len() {
314 if i != j {
315 let distance = self.geometric_distance(i, j)?;
316 if distance > 0.0 {
317 total_distance += distance;
318 reachable_count += 1;
319 }
320 }
321 }
322
323 *centrality_value = if total_distance > 0.0 && reachable_count > 0 {
325 reachable_count as f64 / total_distance
326 } else {
327 0.0
328 };
329 }
330
331 Ok(centrality)
332 }
333
334 pub fn compute_betweenness_centrality(&self) -> NetworkResult<Vec<f64>> {
339 if self.nodes.is_empty() {
340 return Ok(Vec::new());
341 }
342
343 let mut betweenness = vec![0.0; self.nodes.len()];
344 let distances = self.compute_all_pairs_shortest_paths()?;
345
346 for s in 0..self.nodes.len() {
347 for t in 0..self.nodes.len() {
348 if s == t {
349 continue;
350 }
351
352 if distances[s][t].is_infinite() {
353 continue; }
355
356 for v in 0..self.nodes.len() {
358 if v == s || v == t {
359 continue;
360 }
361
362 if !distances[s][v].is_infinite() && !distances[v][t].is_infinite() {
363 let path_through_v = distances[s][v] + distances[v][t];
364
365 if (path_through_v - distances[s][t]).abs() < 1e-10 {
367 betweenness[v] += 1.0;
368 }
369 }
370 }
371 }
372 }
373
374 let normalization = ((self.nodes.len() - 1) * (self.nodes.len() - 2)) as f64;
376 if normalization > 0.0 {
377 for value in &mut betweenness {
378 *value /= normalization;
379 }
380 }
381
382 Ok(betweenness)
383 }
384
385 pub fn compute_all_pairs_shortest_paths(&self) -> NetworkResult<Vec<Vec<f64>>> {
390 let n = self.nodes.len();
391 let mut distances = vec![vec![f64::INFINITY; n]; n];
392
393 for (i, distance_row) in distances.iter_mut().enumerate().take(n) {
395 distance_row[i] = 0.0; }
397
398 for edge in &self.edges {
400 distances[edge.source][edge.target] = edge.weight;
401 }
402
403 for k in 0..n {
405 for i in 0..n {
406 for j in 0..n {
407 if distances[i][k] != f64::INFINITY && distances[k][j] != f64::INFINITY {
408 let new_distance = distances[i][k] + distances[k][j];
409 if new_distance < distances[i][j] {
410 distances[i][j] = new_distance;
411 }
412 }
413 }
414 }
415 }
416
417 Ok(distances)
418 }
419
420 pub fn shortest_path(
425 &self,
426 source: usize,
427 target: usize,
428 ) -> NetworkResult<Option<(Vec<usize>, f64)>> {
429 if source >= self.nodes.len() {
430 return Err(NetworkError::NodeIndexOutOfBounds(source));
431 }
432 if target >= self.nodes.len() {
433 return Err(NetworkError::NodeIndexOutOfBounds(target));
434 }
435
436 if source == target {
437 return Ok(Some((vec![source], 0.0)));
438 }
439
440 let n = self.nodes.len();
441 let mut distances = vec![f64::INFINITY; n];
442 let mut previous = vec![None; n];
443 let mut visited = vec![false; n];
444
445 distances[source] = 0.0;
446
447 let mut adj_list: Vec<Vec<(usize, f64)>> = vec![Vec::new(); n];
449 for edge in &self.edges {
450 adj_list[edge.source].push((edge.target, edge.weight));
451 }
452
453 for _ in 0..n {
454 let mut current = None;
456 let mut min_distance = f64::INFINITY;
457
458 for v in 0..n {
459 if !visited[v] && distances[v] < min_distance {
460 min_distance = distances[v];
461 current = Some(v);
462 }
463 }
464
465 let current = match current {
466 Some(v) => v,
467 None => break, };
469
470 if current == target {
471 break; }
473
474 visited[current] = true;
475
476 for &(neighbor, weight) in &adj_list[current] {
478 if !visited[neighbor] {
479 let new_distance = distances[current] + weight;
480 if new_distance < distances[neighbor] {
481 distances[neighbor] = new_distance;
482 previous[neighbor] = Some(current);
483 }
484 }
485 }
486 }
487
488 if distances[target].is_infinite() {
490 return Ok(None);
491 }
492
493 let mut path = Vec::new();
495 let mut current = target;
496
497 while let Some(prev) = previous[current] {
498 path.push(current);
499 current = prev;
500 }
501 path.push(source);
502 path.reverse();
503
504 Ok(Some((path, distances[target])))
505 }
506
507 pub fn shortest_geometric_path(
512 &self,
513 source: usize,
514 target: usize,
515 ) -> NetworkResult<Option<(Vec<usize>, f64)>> {
516 if source >= self.nodes.len() {
517 return Err(NetworkError::NodeIndexOutOfBounds(source));
518 }
519 if target >= self.nodes.len() {
520 return Err(NetworkError::NodeIndexOutOfBounds(target));
521 }
522
523 if source == target {
524 return Ok(Some((vec![source], 0.0)));
525 }
526
527 let n = self.nodes.len();
528 let mut distances = vec![f64::INFINITY; n];
529 let mut previous = vec![None; n];
530 let mut visited = vec![false; n];
531
532 distances[source] = 0.0;
533
534 for _ in 0..n {
535 let mut current = None;
537 let mut min_distance = f64::INFINITY;
538
539 for v in 0..n {
540 if !visited[v] && distances[v] < min_distance {
541 min_distance = distances[v];
542 current = Some(v);
543 }
544 }
545
546 let current = match current {
547 Some(v) => v,
548 None => break,
549 };
550
551 if current == target {
552 break;
553 }
554
555 visited[current] = true;
556
557 for edge in &self.edges {
559 if edge.source == current {
560 let neighbor = edge.target;
561 if !visited[neighbor] {
562 let geometric_distance = self.geometric_distance(current, neighbor)?;
563 let new_distance = distances[current] + geometric_distance;
564
565 if new_distance < distances[neighbor] {
566 distances[neighbor] = new_distance;
567 previous[neighbor] = Some(current);
568 }
569 }
570 }
571 }
572 }
573
574 if distances[target].is_infinite() {
575 return Ok(None);
576 }
577
578 let mut path = Vec::new();
580 let mut current = target;
581
582 while let Some(prev) = previous[current] {
583 path.push(current);
584 current = prev;
585 }
586 path.push(source);
587 path.reverse();
588
589 Ok(Some((path, distances[target])))
590 }
591
592 pub fn to_tropical_network(&self) -> NetworkResult<crate::tropical::TropicalNetwork> {
597 let n = self.nodes.len();
598 let mut weights = vec![vec![f64::INFINITY; n]; n];
599
600 for (i, weight_row) in weights.iter_mut().enumerate().take(n) {
602 weight_row[i] = 0.0;
603 }
604
605 for edge in &self.edges {
607 weights[edge.source][edge.target] = edge.weight;
608 }
609
610 crate::tropical::TropicalNetwork::from_weights(&weights)
611 }
612
613 pub fn find_communities(
618 &self,
619 num_communities: usize,
620 ) -> NetworkResult<Vec<Community<P, Q, R>>> {
621 if self.nodes.is_empty() {
622 return Ok(Vec::new());
623 }
624
625 if num_communities == 0 {
626 return Err(NetworkError::invalid_param(
627 "num_communities",
628 0,
629 "greater than 0",
630 ));
631 }
632
633 if num_communities >= self.nodes.len() {
634 let communities = self
636 .nodes
637 .iter()
638 .enumerate()
639 .map(|(i, node)| Community {
640 nodes: vec![i],
641 geometric_centroid: node.clone(),
642 cohesion_score: 1.0,
643 })
644 .collect();
645 return Ok(communities);
646 }
647
648 let mut centroids: Vec<Multivector<P, Q, R>> = Vec::with_capacity(num_communities);
650 centroids.push(self.nodes[0].clone());
651
652 for _ in 1..num_communities {
653 let mut max_distance = 0.0;
654 let mut farthest_node = 0;
655
656 for (i, node) in self.nodes.iter().enumerate() {
657 let mut min_distance_to_centroid = f64::INFINITY;
658
659 for centroid in ¢roids {
660 let diff = node.clone() - centroid.clone();
661 let distance = diff.norm();
662 if distance < min_distance_to_centroid {
663 min_distance_to_centroid = distance;
664 }
665 }
666
667 if min_distance_to_centroid > max_distance {
668 max_distance = min_distance_to_centroid;
669 farthest_node = i;
670 }
671 }
672
673 centroids.push(self.nodes[farthest_node].clone());
674 }
675
676 let mut assignments = vec![0; self.nodes.len()];
678 let max_iterations = 100;
679
680 for _ in 0..max_iterations {
681 let mut changed = false;
682
683 for (node_idx, node) in self.nodes.iter().enumerate() {
685 let mut best_cluster = 0;
686 let mut min_distance = f64::INFINITY;
687
688 for (cluster_idx, centroid) in centroids.iter().enumerate() {
689 let diff = node.clone() - centroid.clone();
690 let distance = diff.norm();
691
692 if distance < min_distance {
693 min_distance = distance;
694 best_cluster = cluster_idx;
695 }
696 }
697
698 if assignments[node_idx] != best_cluster {
699 assignments[node_idx] = best_cluster;
700 changed = true;
701 }
702 }
703
704 if !changed {
705 break;
706 }
707
708 for (cluster_idx, centroid) in centroids.iter_mut().enumerate().take(num_communities) {
710 let cluster_nodes: Vec<&Multivector<P, Q, R>> = self
711 .nodes
712 .iter()
713 .enumerate()
714 .filter(|(i, _)| assignments[*i] == cluster_idx)
715 .map(|(_, node)| node)
716 .collect();
717
718 if !cluster_nodes.is_empty() {
719 let mut sum = cluster_nodes[0].clone();
721 for node in cluster_nodes.iter().skip(1) {
722 sum = sum + (*node).clone();
723 }
724 *centroid = sum * (1.0 / cluster_nodes.len() as f64);
725 }
726 }
727 }
728
729 let mut communities = Vec::new();
731
732 for (cluster_idx, centroid) in centroids.iter().enumerate().take(num_communities) {
733 let cluster_nodes: Vec<usize> = assignments
734 .iter()
735 .enumerate()
736 .filter(|(_, &assignment)| assignment == cluster_idx)
737 .map(|(i, _)| i)
738 .collect();
739
740 if cluster_nodes.is_empty() {
741 continue;
742 }
743
744 let mut total_distance = 0.0;
746 let mut pair_count = 0;
747
748 for &i in &cluster_nodes {
749 for &j in &cluster_nodes {
750 if i < j {
751 let distance = self.geometric_distance(i, j)?;
752 total_distance += distance;
753 pair_count += 1;
754 }
755 }
756 }
757
758 let cohesion_score = if pair_count > 0 && total_distance > 0.0 {
759 pair_count as f64 / total_distance
760 } else {
761 1.0
762 };
763
764 communities.push(Community {
765 nodes: cluster_nodes,
766 geometric_centroid: centroid.clone(),
767 cohesion_score,
768 });
769 }
770
771 Ok(communities)
772 }
773
774 pub fn simulate_diffusion(
780 &self,
781 initial_sources: &[usize],
782 max_steps: usize,
783 diffusion_rate: f64,
784 ) -> NetworkResult<PropagationAnalysis> {
785 if initial_sources.is_empty() {
786 return Err(NetworkError::insufficient_data(
787 "No initial sources provided",
788 ));
789 }
790
791 for &source in initial_sources {
792 if source >= self.nodes.len() {
793 return Err(NetworkError::NodeIndexOutOfBounds(source));
794 }
795 }
796
797 if diffusion_rate <= 0.0 || diffusion_rate > 1.0 {
798 return Err(NetworkError::invalid_param(
799 "diffusion_rate",
800 diffusion_rate,
801 "between 0 and 1",
802 ));
803 }
804
805 let n = self.nodes.len();
806 let mut information_levels = vec![0.0; n];
807 let mut coverage = Vec::new();
808 let mut influence_scores = vec![0.0; n];
809
810 for &source in initial_sources {
812 information_levels[source] = 1.0;
813 }
814
815 let convergence_threshold = 1e-6;
816 let mut converged_step = max_steps;
817
818 for step in 0..max_steps {
819 let current_coverage = information_levels
821 .iter()
822 .filter(|&&level| level > convergence_threshold)
823 .count();
824 coverage.push(current_coverage);
825
826 let previous_levels = information_levels.clone();
827 let mut new_levels = information_levels.clone();
828
829 for edge in &self.edges {
831 let source_level = information_levels[edge.source];
832 if source_level > convergence_threshold {
833 let similarity = self.compute_geometric_similarity(edge.source, edge.target)?;
835
836 let transfer_amount = source_level * diffusion_rate * similarity * edge.weight;
838
839 new_levels[edge.target] += transfer_amount;
840 influence_scores[edge.source] += transfer_amount;
841 }
842 }
843
844 for level in &mut new_levels {
846 *level *= 0.95; }
848
849 information_levels = new_levels;
850
851 let total_change: f64 = information_levels
853 .iter()
854 .zip(previous_levels.iter())
855 .map(|(new, old)| (new - old).abs())
856 .sum();
857
858 if total_change < convergence_threshold {
859 converged_step = step + 1;
860 break;
861 }
862 }
863
864 Ok(PropagationAnalysis {
865 coverage,
866 influence_scores,
867 convergence_time: converged_step,
868 })
869 }
870
871 fn compute_geometric_similarity(&self, node1: usize, node2: usize) -> NetworkResult<f64> {
876 let pos1 = &self.nodes[node1];
877 let pos2 = &self.nodes[node2];
878
879 let product = pos1.geometric_product(pos2);
881 let scalar_part = product.scalar_part();
882
883 let norm1 = pos1.norm();
885 let norm2 = pos2.norm();
886
887 if norm1 > 0.0 && norm2 > 0.0 {
888 Ok((scalar_part / (norm1 * norm2)).abs())
889 } else {
890 Ok(0.0)
891 }
892 }
893
894 pub fn spectral_clustering(&self, num_clusters: usize) -> NetworkResult<Vec<Vec<usize>>> {
899 use nalgebra::{DMatrix, SymmetricEigen};
900
901 if self.nodes.is_empty() {
902 return Ok(Vec::new());
903 }
904
905 if num_clusters == 0 {
906 return Err(NetworkError::invalid_param(
907 "num_clusters",
908 0,
909 "greater than 0",
910 ));
911 }
912
913 let n = self.nodes.len();
914 if num_clusters >= n {
915 return Ok((0..n).map(|i| vec![i]).collect());
917 }
918
919 let mut adjacency = DMatrix::zeros(n, n);
921 for edge in &self.edges {
922 adjacency[(edge.source, edge.target)] = edge.weight;
923 adjacency[(edge.target, edge.source)] = edge.weight;
925 }
926
927 let mut degree = DMatrix::zeros(n, n);
929 for i in 0..n {
930 let node_degree: f64 = adjacency.row(i).sum();
931 if node_degree > 0.0 {
932 degree[(i, i)] = node_degree;
933 }
934 }
935
936 let mut normalized_laplacian = DMatrix::identity(n, n);
938
939 for i in 0..n {
940 for j in 0..n {
941 if i != j && degree[(i, i)] > 0.0 && degree[(j, j)] > 0.0 {
942 let value = adjacency[(i, j)] / (degree[(i, i)].sqrt() * degree[(j, j)].sqrt());
943 normalized_laplacian[(i, j)] = -value;
944 }
945 }
946 }
947
948 let eigen = SymmetricEigen::new(normalized_laplacian);
950 let eigenvalues = eigen.eigenvalues;
951 let eigenvectors = eigen.eigenvectors;
952
953 let mut clusters = vec![Vec::new(); num_clusters];
955
956 for i in 0..n {
958 let mut best_cluster = 0;
959 let mut max_value = eigenvectors[(i, 0)];
960
961 for k in 1..num_clusters.min(eigenvalues.len()) {
962 if eigenvectors[(i, k)].abs() > max_value.abs() {
963 max_value = eigenvectors[(i, k)];
964 best_cluster = k;
965 }
966 }
967
968 clusters[best_cluster].push(i);
969 }
970
971 clusters.retain(|cluster| !cluster.is_empty());
973
974 Ok(clusters)
975 }
976}
977
978impl<const P: usize, const Q: usize, const R: usize> Default for GeometricNetwork<P, Q, R> {
979 fn default() -> Self {
980 Self::new()
981 }
982}