1use crate::prelude::simplify;
10use crate::symbolic::core::Expr;
11use crate::symbolic::graph::Graph;
12use crate::symbolic::simplify::as_f64;
13use ordered_float::OrderedFloat;
14use std::collections::{HashMap, HashSet, VecDeque};
15use std::fmt::Debug;
16use std::hash::Hash;
17
18pub fn dfs<V>(graph: &Graph<V>, start_node: usize) -> Vec<usize>
33where
34 V: Eq + Hash + Clone + std::fmt::Debug,
35{
36 let mut visited = HashSet::new();
37 let mut result = Vec::new();
38 dfs_recursive(graph, start_node, &mut visited, &mut result);
39 result
40}
41
42pub(crate) fn dfs_recursive<V>(
43 graph: &Graph<V>,
44 u: usize,
45 visited: &mut HashSet<usize>,
46 result: &mut Vec<usize>,
47) where
48 V: Eq + Hash + Clone + std::fmt::Debug,
49{
50 visited.insert(u);
51 result.push(u);
52 if let Some(neighbors) = graph.adj.get(u) {
53 for &(v, _) in neighbors {
54 if !visited.contains(&v) {
55 dfs_recursive(graph, v, visited, result);
56 }
57 }
58 }
59}
60
61pub fn bfs<V>(graph: &Graph<V>, start_node: usize) -> Vec<usize>
72where
73 V: Eq + Hash + Clone + std::fmt::Debug,
74{
75 let mut visited = HashSet::new();
76 let mut result = Vec::new();
77 let mut queue = VecDeque::new();
78
79 visited.insert(start_node);
80 queue.push_back(start_node);
81
82 while let Some(u) = queue.pop_front() {
83 result.push(u);
84 if let Some(neighbors) = graph.adj.get(u) {
85 for &(v, _) in neighbors {
86 if !visited.contains(&v) {
87 visited.insert(v);
88 queue.push_back(v);
89 }
90 }
91 }
92 }
93 result
94}
95
96pub fn connected_components<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
111 graph: &Graph<V>,
112) -> Vec<Vec<usize>> {
113 let mut visited = HashSet::new();
114 let mut components = Vec::new();
115 for node_id in 0..graph.nodes.len() {
116 if !visited.contains(&node_id) {
117 let component = bfs(graph, node_id);
118 for &visited_node in &component {
119 visited.insert(visited_node);
120 }
121 components.push(component);
122 }
123 }
124 components
125}
126
127pub fn is_connected<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(graph: &Graph<V>) -> bool {
138 connected_components(graph).len() == 1
139}
140
141pub fn strongly_connected_components<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
152 graph: &Graph<V>,
153) -> Vec<Vec<usize>> {
154 let mut scc = Vec::new();
155 let mut stack = Vec::new();
156 let mut on_stack = HashSet::new();
157 let mut discovery_times = HashMap::new();
158 let mut low_link = HashMap::new();
159 let mut time = 0;
160 for node_id in 0..graph.nodes.len() {
161 tarjan_scc_util(
162 graph,
163 node_id,
164 &mut time,
165 &mut discovery_times,
166 &mut low_link,
167 &mut stack,
168 &mut on_stack,
169 &mut scc,
170 );
171 }
172 scc
173}
174
175pub(crate) fn tarjan_scc_util<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
176 graph: &Graph<V>,
177 u: usize,
178 time: &mut usize,
179 disc: &mut HashMap<usize, usize>,
180 low: &mut HashMap<usize, usize>,
181 stack: &mut Vec<usize>,
182 on_stack: &mut HashSet<usize>,
183 scc: &mut Vec<Vec<usize>>,
184) {
185 disc.insert(u, *time);
186 low.insert(u, *time);
187 *time += 1;
188 stack.push(u);
189 on_stack.insert(u);
190
191 if let Some(neighbors) = graph.adj.get(u) {
192 for &(v, _) in neighbors {
193 if !disc.contains_key(&v) {
194 tarjan_scc_util(graph, v, time, disc, low, stack, on_stack, scc);
195 let low_u = *low.get(&u).unwrap();
196 let low_v = *low.get(&v).unwrap();
197 low.insert(u, low_u.min(low_v));
198 } else if on_stack.contains(&v) {
199 let low_u = *low.get(&u).unwrap();
200 let disc_v = *disc.get(&v).unwrap();
201 low.insert(u, low_u.min(disc_v));
202 }
203 }
204 }
205
206 if low.get(&u) == disc.get(&u) {
207 let mut component = Vec::new();
208 while let Some(top) = stack.pop() {
209 on_stack.remove(&top);
210 component.push(top);
211 if top == u {
212 break;
213 }
214 }
215 scc.push(component);
216 }
217}
218
219pub fn has_cycle<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(graph: &Graph<V>) -> bool {
234 let mut visited = HashSet::new();
235 if graph.is_directed {
236 let mut recursion_stack = HashSet::new();
237 for node_id in 0..graph.nodes.len() {
238 if !visited.contains(&node_id) {
239 if has_cycle_directed_util(graph, node_id, &mut visited, &mut recursion_stack) {
240 return true;
241 }
242 }
243 }
244 } else {
245 for node_id in 0..graph.nodes.len() {
246 if !visited.contains(&node_id) {
247 if has_cycle_undirected_util(graph, node_id, &mut visited, None) {
248 return true;
249 }
250 }
251 }
252 }
253 false
254}
255
256pub(crate) fn has_cycle_directed_util<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
257 graph: &Graph<V>,
258 u: usize,
259 visited: &mut HashSet<usize>,
260 rec_stack: &mut HashSet<usize>,
261) -> bool {
262 visited.insert(u);
263 rec_stack.insert(u);
264 if let Some(neighbors) = graph.adj.get(u) {
265 for &(v, _) in neighbors {
266 if !visited.contains(&v) {
267 if has_cycle_directed_util(graph, v, visited, rec_stack) {
268 return true;
269 }
270 } else if rec_stack.contains(&v) {
271 return true;
272 }
273 }
274 }
275 rec_stack.remove(&u);
276 false
277}
278
279pub(crate) fn has_cycle_undirected_util<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
280 graph: &Graph<V>,
281 u: usize,
282 visited: &mut HashSet<usize>,
283 parent: Option<usize>,
284) -> bool {
285 visited.insert(u);
286 if let Some(neighbors) = graph.adj.get(u) {
287 for &(v, _) in neighbors {
288 if !visited.contains(&v) {
289 if has_cycle_undirected_util(graph, v, visited, Some(u)) {
290 return true;
291 }
292 } else if Some(v) != parent {
293 return true;
294 }
295 }
296 }
297 false
298}
299
300pub fn find_bridges_and_articulation_points<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
313 graph: &Graph<V>,
314) -> (Vec<(usize, usize)>, Vec<usize>) {
315 let mut bridges = Vec::new();
316 let mut articulation_points = HashSet::new();
317 let mut visited = HashSet::new();
318 let mut discovery_times = HashMap::new();
319 let mut low_link = HashMap::new();
320 let mut time = 0;
321
322 for node_id in 0..graph.nodes.len() {
323 if !visited.contains(&node_id) {
324 b_and_ap_util(
325 graph,
326 node_id,
327 None,
328 &mut time,
329 &mut visited,
330 &mut discovery_times,
331 &mut low_link,
332 &mut bridges,
333 &mut articulation_points,
334 );
335 }
336 }
337 (bridges, articulation_points.into_iter().collect())
338}
339
340pub(crate) fn b_and_ap_util<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
341 graph: &Graph<V>,
342 u: usize,
343 parent: Option<usize>,
344 time: &mut usize,
345 visited: &mut HashSet<usize>,
346 disc: &mut HashMap<usize, usize>,
347 low: &mut HashMap<usize, usize>,
348 bridges: &mut Vec<(usize, usize)>,
349 ap: &mut HashSet<usize>,
350) {
351 visited.insert(u);
352 disc.insert(u, *time);
353 low.insert(u, *time);
354 *time += 1;
355 let mut children = 0;
356
357 if let Some(neighbors) = graph.adj.get(u) {
358 for &(v, _) in neighbors {
359 if Some(v) == parent {
360 continue;
361 }
362 if visited.contains(&v) {
363 low.insert(u, (*low.get(&u).unwrap()).min(*disc.get(&v).unwrap()));
364 } else {
365 children += 1;
366 b_and_ap_util(graph, v, Some(u), time, visited, disc, low, bridges, ap);
367 low.insert(u, (*low.get(&u).unwrap()).min(*low.get(&v).unwrap()));
368
369 if parent.is_some() && low.get(&v).unwrap() >= disc.get(&u).unwrap() {
370 ap.insert(u);
371 }
372 if low.get(&v).unwrap() > disc.get(&u).unwrap() {
373 bridges.push((u, v));
374 }
375 }
376 }
377 }
378
379 if parent.is_none() && children > 1 {
380 ap.insert(u);
381 }
382}
383
384pub struct DSU {
390 parent: Vec<usize>,
391}
392
393impl DSU {
394 pub(crate) fn new(n: usize) -> Self {
395 DSU {
396 parent: (0..n).collect(),
397 }
398 }
399
400 pub(crate) fn find(&mut self, i: usize) -> usize {
401 if self.parent[i] == i {
402 return i;
403 }
404 self.parent[i] = self.find(self.parent[i]);
405 self.parent[i]
406 }
407
408 pub(crate) fn union(&mut self, i: usize, j: usize) {
409 let root_i = self.find(i);
410 let root_j = self.find(j);
411 if root_i != root_j {
412 self.parent[root_i] = root_j;
413 }
414 }
415}
416
417pub fn kruskal_mst<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
429 graph: &Graph<V>,
430) -> Vec<(usize, usize, Expr)> {
431 let mut edges = graph.get_edges();
432
433 edges.sort_by(|a, b| {
434 let weight_a = as_f64(&a.2).unwrap_or(f64::INFINITY);
435 let weight_b = as_f64(&b.2).unwrap_or(f64::INFINITY);
436 weight_a.partial_cmp(&weight_b).unwrap()
437 });
438
439 let mut dsu = DSU::new(graph.nodes.len());
440 let mut mst = Vec::new();
441
442 for (u, v, weight) in edges {
443 if dsu.find(u) != dsu.find(v) {
444 dsu.union(u, v);
445 mst.push((u, v, weight));
446 }
447 }
448 mst
449}
450
451pub fn edmonds_karp_max_flow<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
469 capacity_graph: &Graph<V>,
470 s: usize,
471 t: usize,
472) -> f64 {
473 let n = capacity_graph.nodes.len();
474 let mut residual_capacity = vec![vec![0.0; n]; n];
475 for u in 0..n {
476 if let Some(neighbors) = capacity_graph.adj.get(u) {
477 for &(v, ref cap) in neighbors {
478 residual_capacity[u][v] = as_f64(cap).unwrap_or(0.0);
479 }
480 }
481 }
482
483 let mut max_flow = 0.0;
484
485 loop {
486 let (parent, path_flow) = bfs_for_augmenting_path(&residual_capacity, s, t);
487
488 if path_flow == 0.0 {
489 break; }
491
492 max_flow += path_flow;
493 let mut v = t;
494 while v != s {
495 let u = parent[v].unwrap();
496 residual_capacity[u][v] -= path_flow;
497 residual_capacity[v][u] += path_flow;
498 v = u;
499 }
500 }
501
502 max_flow
503}
504
505pub(crate) fn bfs_for_augmenting_path(
507 capacity: &Vec<Vec<f64>>,
508 s: usize,
509 t: usize,
510) -> (Vec<Option<usize>>, f64) {
511 let n = capacity.len();
512 let mut parent = vec![None; n];
513 let mut queue = VecDeque::new();
514 let mut path_flow = vec![f64::INFINITY; n];
515
516 queue.push_back(s);
517
518 while let Some(u) = queue.pop_front() {
519 for v in 0..n {
520 if parent[v].is_none() && v != s && capacity[u][v] > 0.0 {
521 parent[v] = Some(u);
522 path_flow[v] = path_flow[u].min(capacity[u][v]);
523 if v == t {
524 return (parent, path_flow[t]);
525 }
526 queue.push_back(v);
527 }
528 }
529 }
530
531 (parent, 0.0)
532}
533
534pub fn dinic_max_flow<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
549 capacity_graph: &Graph<V>,
550 s: usize,
551 t: usize,
552) -> f64 {
553 let n = capacity_graph.nodes.len();
554 let mut residual_capacity = vec![vec![0.0; n]; n];
555 for u in 0..n {
556 if let Some(neighbors) = capacity_graph.adj.get(u) {
557 for &(v, ref cap) in neighbors {
558 residual_capacity[u][v] = as_f64(cap).unwrap_or(0.0);
559 }
560 }
561 }
562
563 let mut max_flow = 0.0;
564 let mut level = vec![0; n];
565
566 while dinic_bfs(&residual_capacity, s, t, &mut level) {
567 let mut ptr = vec![0; n];
568 while {
569 let pushed = dinic_dfs(
570 &mut residual_capacity,
571 s,
572 t,
573 f64::INFINITY,
574 &level,
575 &mut ptr,
576 );
577 if pushed > 0.0 {
578 max_flow += pushed;
579 true
580 } else {
581 false
582 }
583 } {}
584 }
585
586 max_flow
587}
588
589pub(crate) fn dinic_bfs(
590 capacity: &Vec<Vec<f64>>,
591 s: usize,
592 t: usize,
593 level: &mut Vec<i32>,
594) -> bool {
595 level.iter_mut().for_each(|l| *l = -1);
596 level[s] = 0;
597 let mut q = VecDeque::new();
598 q.push_back(s);
599
600 while let Some(u) = q.pop_front() {
601 for v in 0..capacity.len() {
602 if level[v] < 0 && capacity[u][v] > 0.0 {
603 level[v] = level[u] + 1;
604 q.push_back(v);
605 }
606 }
607 }
608 level[t] != -1
609}
610
611pub(crate) fn dinic_dfs(
612 cap: &mut Vec<Vec<f64>>,
613 u: usize,
614 t: usize,
615 pushed: f64,
616 level: &Vec<i32>,
617 ptr: &mut Vec<usize>,
618) -> f64 {
619 if pushed == 0.0 {
620 return 0.0;
621 }
622 if u == t {
623 return pushed;
624 }
625
626 while ptr[u] < cap.len() {
627 let v = ptr[u];
628 if level[v] != level[u] + 1 || cap[u][v] == 0.0 {
629 ptr[u] += 1;
630 continue;
631 }
632 let tr = dinic_dfs(cap, v, t, pushed.min(cap[u][v]), level, ptr);
633 if tr == 0.0 {
634 ptr[u] += 1;
635 continue;
636 }
637 cap[u][v] -= tr;
638 cap[v][u] += tr;
639 return tr;
640 }
641 0.0
642}
643
644pub fn bellman_ford<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
659 graph: &Graph<V>,
660 start_node: usize,
661) -> Result<(HashMap<usize, f64>, HashMap<usize, Option<usize>>), String> {
662 let n = graph.nodes.len();
663 let mut dist = HashMap::new();
664 let mut prev = HashMap::new();
665
666 for node_id in 0..graph.nodes.len() {
667 dist.insert(node_id, f64::INFINITY);
668 }
669 dist.insert(start_node, 0.0);
670
671 for _ in 1..n {
672 for u in 0..n {
673 if let Some(neighbors) = graph.adj.get(u) {
674 for &(v, ref weight) in neighbors {
675 let w = as_f64(weight).unwrap_or(f64::INFINITY);
676 if dist[&u] != f64::INFINITY && dist[&u] + w < dist[&v] {
677 dist.insert(v, dist[&u] + w);
678 prev.insert(v, Some(u));
679 }
680 }
681 }
682 }
683 }
684
685 for u in 0..n {
687 if let Some(neighbors) = graph.adj.get(u) {
688 for &(v, ref weight) in neighbors {
689 let w = as_f64(weight).unwrap_or(f64::INFINITY);
690 if dist[&u] != f64::INFINITY && dist[&u] + w < dist[&v] {
691 return Err("Graph contains a negative-weight cycle.".to_string());
692 }
693 }
694 }
695 }
696
697 Ok((dist, prev))
698}
699
700#[allow(unused_variables)]
714pub fn min_cost_max_flow<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
715 graph: &Graph<V>,
716 s: usize,
717 t: usize,
718) -> (f64, f64) {
719 let n = graph.nodes.len();
720 let mut capacity = vec![vec![0.0; n]; n];
721 let mut cost = vec![vec![0.0; n]; n];
722
723 for u in 0..n {
724 if let Some(neighbors) = graph.adj.get(u) {
725 for &(v, ref weight) in neighbors {
726 if let Expr::Tuple(t) = weight {
727 if t.len() == 2 {
728 capacity[u][v] = as_f64(&t[0]).unwrap_or(0.0);
729 cost[u][v] = as_f64(&t[1]).unwrap_or(0.0);
730 }
731 }
732 }
733 }
734 }
735
736 let mut flow = 0.0;
737 let mut total_cost = 0.0;
738
739 loop {
740 let mut dist = vec![f64::INFINITY; n];
742 let mut parent = vec![None; n];
743 dist[s] = 0.0;
744
745 for _ in 1..n {
746 for u in 0..n {
747 for v in 0..n {
748 if capacity[u][v] > 0.0
749 && dist[u] != f64::INFINITY
750 && dist[u] + cost[u][v] < dist[v]
751 {
752 dist[v] = dist[u] + cost[u][v];
753 parent[v] = Some(u);
754 }
755 }
756 }
757 }
758
759 if dist[t] == f64::INFINITY {
760 break; }
762
763 let mut path_flow = f64::INFINITY;
765 let mut curr = t;
766 while let Some(prev) = parent[curr] {
767 path_flow = path_flow.min(capacity[prev][curr]);
768 curr = prev;
769 }
770
771 flow += path_flow;
773 total_cost += path_flow * dist[t];
774 let mut v = t;
775 while let Some(u) = parent[v] {
776 capacity[u][v] -= path_flow;
777 capacity[v][u] += path_flow;
778 v = u;
781 }
782 }
783
784 (0.0, 0.0) }
786
787pub fn is_bipartite<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
803 graph: &Graph<V>,
804) -> Option<Vec<i8>> {
805 let n = graph.nodes.len();
806 let mut colors = vec![-1; n]; for i in 0..n {
809 if colors[i] == -1 {
810 let mut queue = VecDeque::new();
811 queue.push_back(i);
812 colors[i] = 0;
813
814 while let Some(u) = queue.pop_front() {
815 if let Some(neighbors) = graph.adj.get(u) {
816 for &(v, _) in neighbors {
817 if colors[v] == -1 {
818 colors[v] = 1 - colors[u];
819 queue.push_back(v);
820 } else if colors[v] == colors[u] {
821 return None; }
823 }
824 }
825 }
826 }
827 }
828 Some(colors)
829}
830
831pub fn bipartite_maximum_matching<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
844 graph: &Graph<V>,
845 partition: &[i8],
846) -> Vec<(usize, usize)> {
847 let n = graph.nodes.len();
848 let s = n; let t = n + 1; let mut flow_graph = Graph::new(true);
851
852 let mut u_nodes = Vec::new();
853 let mut v_nodes = Vec::new();
854 for i in 0..n {
855 if partition[i] == 0 {
856 u_nodes.push(i);
857 } else {
858 v_nodes.push(i);
859 }
860 }
861
862 for &u in &u_nodes {
864 flow_graph.add_edge(&s, &u, Expr::Constant(1.0));
865 if let Some(neighbors) = graph.adj.get(u) {
866 for &(v, _) in neighbors {
867 flow_graph.add_edge(&u, &v, Expr::Constant(1.0));
868 }
869 }
870 }
871 for &v in &v_nodes {
872 flow_graph.add_edge(&v, &t, Expr::Constant(1.0));
873 }
874
875 let _max_flow = edmonds_karp_max_flow(&flow_graph, s, t);
880 vec![]
881}
882
883pub fn prim_mst<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
900 graph: &Graph<V>,
901 start_node: usize,
902) -> Vec<(usize, usize, Expr)> {
903 let n = graph.nodes.len();
904 let mut mst = Vec::new();
905 let mut visited = vec![false; n];
906 let mut pq = std::collections::BinaryHeap::new();
907
908 visited[start_node] = true;
909 if let Some(neighbors) = graph.adj.get(start_node) {
910 for &(v, ref weight) in neighbors {
911 let cost = as_f64(weight).unwrap_or(f64::INFINITY);
912 pq.push((
913 ordered_float::OrderedFloat(-cost),
914 start_node,
915 v,
916 weight.clone(),
917 ));
918 }
919 }
920
921 while let Some((_, u, v, weight)) = pq.pop() {
922 if visited[v] {
923 continue;
924 }
925 visited[v] = true;
926 mst.push((u, v, weight));
927
928 if let Some(neighbors) = graph.adj.get(v) {
929 for &(next_v, ref next_weight) in neighbors {
930 if !visited[next_v] {
931 let cost = as_f64(next_weight).unwrap_or(f64::INFINITY);
932 pq.push((
933 ordered_float::OrderedFloat(-cost),
934 v,
935 next_v,
936 next_weight.clone(),
937 ));
938 }
939 }
940 }
941 }
942 mst
943}
944
945pub fn topological_sort_kahn<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
958 graph: &Graph<V>,
959) -> Result<Vec<usize>, String> {
960 if !graph.is_directed {
961 return Err("Topological sort is only defined for directed graphs.".to_string());
962 }
963 let n = graph.nodes.len();
964 let mut in_degree = vec![0; n];
965 for i in 0..n {
966 in_degree[i] = graph.in_degree(i);
967 }
968
969 let mut queue: VecDeque<usize> = (0..n).filter(|&i| in_degree[i] == 0).collect();
970 let mut sorted_order = Vec::new();
971
972 while let Some(u) = queue.pop_front() {
973 sorted_order.push(u);
974 if let Some(neighbors) = graph.adj.get(u) {
975 for &(v, _) in neighbors {
976 in_degree[v] -= 1;
977 if in_degree[v] == 0 {
978 queue.push_back(v);
979 }
980 }
981 }
982 }
983
984 if sorted_order.len() == n {
985 Ok(sorted_order)
986 } else {
987 Err("Graph has a cycle, topological sort is not possible.".to_string())
988 }
989}
990
991pub fn topological_sort_dfs<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
1002 graph: &Graph<V>,
1003) -> Vec<usize> {
1004 let mut visited = HashSet::new();
1005 let mut stack = Vec::new();
1006 for node_id in 0..graph.nodes.len() {
1007 if !visited.contains(&node_id) {
1008 topo_dfs_util(graph, node_id, &mut visited, &mut stack);
1009 }
1010 }
1011 stack.reverse();
1012 stack
1013}
1014
1015pub(crate) fn topo_dfs_util<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
1016 graph: &Graph<V>,
1017 u: usize,
1018 visited: &mut HashSet<usize>,
1019 stack: &mut Vec<usize>,
1020) {
1021 visited.insert(u);
1022 if let Some(neighbors) = graph.adj.get(u) {
1023 for &(v, _) in neighbors {
1024 if !visited.contains(&v) {
1025 topo_dfs_util(graph, v, visited, stack);
1026 }
1027 }
1028 }
1029 stack.push(u);
1030}
1031
1032pub fn bipartite_minimum_vertex_cover<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
1046 graph: &Graph<V>,
1047 partition: &[i8],
1048 matching: &[(usize, usize)],
1049) -> Vec<usize> {
1050 let mut u_nodes = HashSet::new();
1051 let mut matched_nodes_u = HashSet::new();
1052 for i in 0..partition.len() {
1053 if partition[i] == 0 {
1054 u_nodes.insert(i);
1055 }
1056 }
1057 for &(u, v) in matching {
1058 if u_nodes.contains(&u) {
1059 matched_nodes_u.insert(u);
1060 } else {
1061 matched_nodes_u.insert(v);
1062 }
1063 }
1064
1065 let unmatched_u: Vec<_> = u_nodes.difference(&matched_nodes_u).cloned().collect();
1066
1067 let mut visited = HashSet::new();
1069 let mut queue = VecDeque::from(unmatched_u);
1070 while let Some(u) = queue.pop_front() {
1071 if visited.contains(&u) {
1072 continue;
1073 }
1074 visited.insert(u);
1075 if let Some(neighbors) = graph.adj.get(u) {
1076 for &(v, _) in neighbors {
1077 if !matching.contains(&(u, v))
1079 && !matching.contains(&(v, u))
1080 && !visited.contains(&v)
1081 {
1082 queue.push_back(v);
1083 }
1084 }
1085 }
1086 }
1087
1088 let mut cover = Vec::new();
1090 for u in u_nodes {
1091 if !visited.contains(&u) {
1092 cover.push(u);
1093 }
1094 }
1095 for i in 0..partition.len() {
1096 if partition[i] == 1 && visited.contains(&i) {
1097 cover.push(i);
1098 }
1099 }
1100 cover
1101}
1102
1103#[allow(unused_variables)]
1115pub fn hopcroft_karp_bipartite_matching<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
1116 graph: &Graph<V>,
1117 partition: &[i8],
1118) -> Vec<(usize, usize)> {
1119 let n = graph.nodes.len();
1120 let mut u_nodes = Vec::new();
1121 for i in 0..n {
1122 if partition[i] == 0 {
1123 u_nodes.push(i);
1124 }
1125 }
1126
1127 let mut pair_u = vec![None; n];
1128 let mut pair_v = vec![None; n];
1129 let mut dist = vec![0; n];
1130 let mut matching = 0;
1131
1132 while hopcroft_karp_bfs(graph, &u_nodes, &mut pair_u, &mut pair_v, &mut dist) {
1133 for &u in &u_nodes {
1134 if pair_u[u].is_none() {
1135 if hopcroft_karp_dfs(graph, u, &mut pair_u, &mut pair_v, &mut dist) {
1136 matching += 1;
1137 }
1138 }
1139 }
1140 }
1141
1142 let mut result = Vec::new();
1143 for u in 0..n {
1144 if let Some(v) = pair_u[u] {
1145 result.push((u, v));
1146 }
1147 }
1148 result
1149}
1150
1151pub(crate) fn hopcroft_karp_bfs<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
1152 graph: &Graph<V>,
1153 u_nodes: &[usize],
1154 pair_u: &mut [Option<usize>],
1155 pair_v: &mut [Option<usize>],
1156 dist: &mut [usize],
1157) -> bool {
1158 let mut queue = VecDeque::new();
1159 for &u in u_nodes {
1160 if pair_u[u].is_none() {
1161 dist[u] = 0;
1162 queue.push_back(u);
1163 } else {
1164 dist[u] = usize::MAX;
1165 }
1166 }
1167 let mut found_path = false;
1168
1169 while let Some(u) = queue.pop_front() {
1170 if let Some(neighbors) = graph.adj.get(u) {
1171 for &(v, _) in neighbors {
1172 if let Some(next_u_opt) = pair_v.get(v) {
1173 if let Some(next_u) = next_u_opt {
1174 if dist[*next_u] == usize::MAX {
1175 dist[*next_u] = dist[u] + 1;
1176 queue.push_back(*next_u);
1177 }
1178 } else {
1179 found_path = true;
1181 }
1182 }
1183 }
1184 }
1185 }
1186 found_path
1187}
1188
1189pub(crate) fn hopcroft_karp_dfs<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
1190 graph: &Graph<V>,
1191 u: usize,
1192 pair_u: &mut [Option<usize>],
1193 pair_v: &mut [Option<usize>],
1194 dist: &mut [usize],
1195) -> bool {
1196 if let Some(neighbors) = graph.adj.get(u) {
1197 for &(v, _) in neighbors {
1198 if let Some(next_u_opt) = pair_v.get(v) {
1199 if let Some(next_u) = next_u_opt {
1200 if dist[*next_u] == dist[u] + 1 {
1201 if hopcroft_karp_dfs(graph, *next_u, pair_u, pair_v, dist) {
1202 pair_v[v] = Some(u);
1203 pair_u[u] = Some(v);
1204 return true;
1205 }
1206 }
1207 } else {
1208 pair_v[v] = Some(u);
1210 pair_u[u] = Some(v);
1211 return true;
1212 }
1213 }
1214 }
1215 }
1216 dist[u] = usize::MAX;
1217 false
1218}
1219
1220#[allow(unused_variables)]
1232pub fn blossom_algorithm<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
1233 graph: &Graph<V>,
1234) -> Vec<(usize, usize)> {
1235 let n = graph.nodes.len();
1236 let mut matching = vec![None; n];
1237 let mut matches = 0;
1238
1239 for i in 0..n {
1240 if matching[i].is_none() {
1241 let path = find_augmenting_path_with_blossoms(graph, i, &matching);
1242 if !path.is_empty() {
1243 matches += 1;
1244 let mut u = path[0];
1245 for &v in path.iter().skip(1) {
1246 matching[u] = Some(v);
1247 matching[v] = Some(u);
1248 u = v;
1249 }
1250 }
1251 }
1252 }
1253
1254 let mut result = Vec::new();
1255 for u in 0..n {
1256 if let Some(v) = matching[u] {
1257 if u < v {
1258 result.push((u, v));
1259 }
1260 }
1261 }
1262 result
1263}
1264
1265pub(crate) fn find_augmenting_path_with_blossoms<
1266 V: Eq + std::hash::Hash + Clone + std::fmt::Debug,
1267>(
1268 graph: &Graph<V>,
1269 start_node: usize,
1270 matching: &[Option<usize>],
1271) -> Vec<usize> {
1272 let n = graph.nodes.len();
1273 let mut parent = vec![None; n];
1274 let mut origin = (0..n).collect::<Vec<_>>();
1275 let mut level = vec![-1; n];
1276 let mut queue = VecDeque::new();
1277
1278 level[start_node] = 0;
1279 queue.push_back(start_node);
1280
1281 while let Some(u) = queue.pop_front() {
1282 if let Some(neighbors) = graph.adj.get(u) {
1283 for &(v, _) in neighbors {
1284 if level[v] == -1 {
1285 if let Some(w) = matching[v] {
1287 parent[v] = Some(u);
1288 level[v] = 1;
1289 level[w] = 0;
1290 queue.push_back(w);
1291 } else {
1292 parent[v] = Some(u);
1294 let mut path = vec![v, u];
1295 let mut curr = u;
1296 while let Some(p) = parent[curr] {
1297 path.push(p);
1298 curr = p;
1299 }
1300 return path;
1301 }
1302 } else if level[v] == 0 {
1303 let base = find_common_ancestor(&origin, &parent, u, v);
1305 contract_blossom::<V>(
1306 base,
1307 u,
1308 v,
1309 &mut queue,
1310 &mut level,
1311 &mut origin,
1312 &mut parent,
1313 matching,
1314 );
1315 contract_blossom::<V>(
1316 base,
1317 v,
1318 u,
1319 &mut queue,
1320 &mut level,
1321 &mut origin,
1322 &mut parent,
1323 matching,
1324 );
1325 }
1326 }
1327 }
1328 }
1329 vec![] }
1331
1332pub(crate) fn find_common_ancestor(
1333 origin: &[usize],
1334 parent: &[Option<usize>],
1335 mut u: usize,
1336 mut v: usize,
1337) -> usize {
1338 let mut visited = vec![false; origin.len()];
1339 loop {
1340 u = origin[u];
1341 visited[u] = true;
1342 if parent[u].is_none() {
1343 break;
1344 }
1345 u = parent[u].unwrap();
1346 }
1347 loop {
1348 v = origin[v];
1349 if visited[v] {
1350 return v;
1351 }
1352 v = parent[v].unwrap();
1353 }
1354}
1355
1356pub(crate) fn contract_blossom<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
1357 base: usize,
1358 mut u: usize,
1359 v: usize,
1360 queue: &mut VecDeque<usize>,
1361 level: &mut Vec<i32>,
1362 origin: &mut [usize],
1363 parent: &mut Vec<Option<usize>>,
1364 matching: &[Option<usize>],
1365) {
1366 while origin[u] != base {
1367 parent[u] = Some(v);
1368 origin[u] = base;
1369 if let Some(w) = matching[u] {
1370 if level[w] == -1 {
1371 level[w] = 0;
1372 queue.push_back(w);
1373 }
1374 }
1375 u = parent[u].unwrap();
1376 }
1377}
1378
1379pub fn shortest_path_unweighted<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
1396 graph: &Graph<V>,
1397 start_node: usize,
1398) -> HashMap<usize, (usize, Option<usize>)> {
1399 let mut distances = HashMap::new();
1400 let mut predecessors = HashMap::new();
1401 let mut queue = VecDeque::new();
1402
1403 distances.insert(start_node, 0);
1404 predecessors.insert(start_node, None);
1405 queue.push_back(start_node);
1406
1407 while let Some(u) = queue.pop_front() {
1408 let u_dist = *distances.get(&u).unwrap();
1409 if let Some(neighbors) = graph.adj.get(u) {
1410 for &(v, _) in neighbors {
1411 if !distances.contains_key(&v) {
1412 distances.insert(v, u_dist + 1);
1413 predecessors.insert(v, Some(u));
1414 queue.push_back(v);
1415 }
1416 }
1417 }
1418 }
1419
1420 let mut result = HashMap::new();
1421 for (node, dist) in distances {
1422 result.insert(node, (dist, predecessors.get(&node).cloned().flatten()));
1423 }
1424 result
1425}
1426
1427pub fn dijkstra<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(
1440 graph: &Graph<V>,
1441 start_node: usize,
1442) -> HashMap<usize, (Expr, Option<usize>)> {
1443 let mut dist = HashMap::new();
1444 let mut prev = HashMap::new();
1445 let mut pq = std::collections::BinaryHeap::new();
1446
1447 for node_id in 0..graph.nodes.len() {
1448 dist.insert(node_id, Expr::Infinity);
1449 }
1450
1451 dist.insert(start_node, Expr::Constant(0.0));
1452 prev.insert(start_node, None);
1453 pq.push((OrderedFloat(0.0), start_node));
1455
1456 while let Some((cost, u)) = pq.pop() {
1457 let cost = -cost.0;
1458 if cost > as_f64(dist.get(&u).unwrap()).unwrap_or(f64::INFINITY) {
1459 continue;
1460 }
1461
1462 if let Some(neighbors) = graph.adj.get(u) {
1463 for &(v, ref weight) in neighbors {
1464 let new_dist = simplify(Expr::Add(
1465 Box::new(Expr::Constant(cost)),
1466 Box::new(weight.clone()),
1467 ));
1468 if as_f64(&new_dist).unwrap_or(f64::INFINITY)
1469 < as_f64(dist.get(&v).unwrap()).unwrap_or(f64::INFINITY)
1470 {
1471 dist.insert(v, new_dist.clone());
1472 prev.insert(v, Some(u));
1473 pq.push((OrderedFloat(-as_f64(&new_dist).unwrap()), v));
1474 }
1475 }
1476 }
1477 }
1478
1479 let mut result = HashMap::new();
1480 for (node, d) in dist {
1481 result.insert(node, (d, prev.get(&node).cloned().flatten()));
1482 }
1483 result
1484}
1485
1486pub fn floyd_warshall<V: Eq + std::hash::Hash + Clone + std::fmt::Debug>(graph: &Graph<V>) -> Expr {
1498 let n = graph.nodes.len();
1499 let mut dist = vec![vec![Expr::Infinity; n]; n];
1500
1501 for i in 0..n {
1502 dist[i][i] = Expr::Constant(0.0);
1503 }
1504
1505 for u in 0..n {
1506 if let Some(neighbors) = graph.adj.get(u) {
1507 for &(v, ref weight) in neighbors {
1508 dist[u][v] = weight.clone();
1509 }
1510 }
1511 }
1512
1513 for k in 0..n {
1514 for i in 0..n {
1515 for j in 0..n {
1516 let new_dist = simplify(Expr::Add(
1517 Box::new(dist[i][k].clone()),
1518 Box::new(dist[k][j].clone()),
1519 ));
1520 if as_f64(&dist[i][j]).unwrap_or(f64::INFINITY)
1523 > as_f64(&new_dist).unwrap_or(f64::INFINITY)
1524 {
1525 dist[i][j] = new_dist;
1526 }
1527 }
1528 }
1529 }
1530
1531 Expr::Matrix(dist)
1532}
1533
1534pub fn spectral_analysis(matrix: &Expr) -> Result<(Expr, Expr), String> {
1548 crate::symbolic::matrix::eigen_decomposition(matrix)
1549}
1550
1551pub fn algebraic_connectivity<V>(graph: &Graph<V>) -> Result<Expr, String>
1563where
1564 V: Clone,
1565 V: Debug,
1566 V: Eq,
1567 V: Hash,
1568{
1569 let laplacian = graph.to_laplacian_matrix();
1570 let (eigenvalues, _) = spectral_analysis(&laplacian)?;
1571
1572 if let Expr::Matrix(eig_vec) = eigenvalues {
1573 if eig_vec.len() < 2 {
1574 return Err("Graph has fewer than 2 eigenvalues.".to_string());
1575 }
1576 let mut numerical_eigenvalues = Vec::new();
1579 for val_expr in eig_vec.iter().flatten() {
1580 if let Some(val) = as_f64(val_expr) {
1581 numerical_eigenvalues.push(val);
1582 } else {
1583 return Err("Eigenvalues are not all numerical, cannot sort.".to_string());
1584 }
1585 }
1586 numerical_eigenvalues.sort_by(|a, b| a.partial_cmp(b).unwrap());
1587 Ok(Expr::Constant(numerical_eigenvalues[1]))
1588 } else {
1589 Err("Eigenvalue computation did not return a vector.".to_string())
1590 }
1591}