weavatrix_graph/algo/
dag_paths.rs1use super::measure::Measure;
2use super::{WeightedPath, topological_sort_filtered};
3use crate::{GraphError, IndexGraphView, Result, String, Vec};
4use core::cmp::Ordering;
5
6pub fn dag_longest_path<G>(graph: &G) -> Result<Option<WeightedPath<G::Node>>>
14where
15 G: IndexGraphView,
16{
17 dag_weighted_longest_path(graph, |_| Some(1_u64))
18}
19
20pub fn dag_longest_path_filtered<G, F>(
27 graph: &G,
28 mut allows_edge: F,
29) -> Result<Option<WeightedPath<G::Node>>>
30where
31 G: IndexGraphView,
32 F: FnMut(G::Edge) -> bool,
33{
34 dag_weighted_longest_path(graph, |edge| allows_edge(edge).then_some(1_u64))
35}
36
37pub fn dag_longest_path_length<G>(graph: &G) -> Result<Option<u64>>
43where
44 G: IndexGraphView,
45{
46 Ok(dag_longest_path(graph)?.map(|path| path.total_cost()))
47}
48
49pub fn dag_longest_path_length_filtered<G, F>(graph: &G, allows_edge: F) -> Result<Option<u64>>
55where
56 G: IndexGraphView,
57 F: FnMut(G::Edge) -> bool,
58{
59 Ok(dag_longest_path_filtered(graph, allows_edge)?.map(|path| path.total_cost()))
60}
61
62pub fn dag_weighted_longest_path<G, Cost, F>(
71 graph: &G,
72 mut edge_cost: F,
73) -> Result<Option<WeightedPath<G::Node, Cost>>>
74where
75 G: IndexGraphView,
76 Cost: Measure,
77 F: FnMut(G::Edge) -> Option<Cost>,
78{
79 let weights = snapshot_weights(graph, &mut edge_cost)?;
80 let order = topological_sort_filtered(graph, |edge| weights[G::edge_slot(edge)].is_some())
81 .ok_or(GraphError::CyclicGraph {
82 algorithm: "DAG longest path",
83 })?;
84 let Some(&first) = order.first() else {
85 return Ok(None);
86 };
87 let mut distances = vec![None; graph.node_bound()];
88 let mut predecessors = vec![None; graph.node_bound()];
89 for &node in &order {
90 distances[G::node_slot(node)] = Some(Cost::zero());
91 }
92 for &source in &order {
93 let Some(source_cost) = distances[G::node_slot(source)] else {
94 continue;
95 };
96 for edge in graph.outgoing_edges(source) {
97 let Some(weight) = weights[G::edge_slot(edge)] else {
98 continue;
99 };
100 let Some(endpoints) = graph.edge_endpoints(edge) else {
101 continue;
102 };
103 let candidate =
104 source_cost
105 .checked_add(weight)
106 .ok_or(GraphError::ArithmeticOverflow {
107 operation: "DAG longest path",
108 })?;
109 let target_slot = G::node_slot(endpoints.target());
110 let current = distances[target_slot].unwrap_or_else(Cost::zero);
111 if candidate.compare(current) == Some(Ordering::Greater) {
112 distances[target_slot] = Some(candidate);
113 predecessors[target_slot] = Some(source);
114 }
115 }
116 }
117 let end = order.into_iter().skip(1).fold(first, |best, candidate| {
118 let best_cost = distances[G::node_slot(best)].unwrap_or_else(Cost::zero);
119 let candidate_cost = distances[G::node_slot(candidate)].unwrap_or_else(Cost::zero);
120 if candidate_cost.compare(best_cost) == Some(Ordering::Greater) {
121 candidate
122 } else {
123 best
124 }
125 });
126 let total_cost = distances[G::node_slot(end)].unwrap_or_else(Cost::zero);
127 Ok(Some(WeightedPath::from_parts(
128 reconstruct::<G>(end, &predecessors),
129 total_cost,
130 )))
131}
132
133pub fn dag_weighted_longest_path_length<G, Cost, F>(graph: &G, edge_cost: F) -> Result<Option<Cost>>
139where
140 G: IndexGraphView,
141 Cost: Measure,
142 F: FnMut(G::Edge) -> Option<Cost>,
143{
144 Ok(dag_weighted_longest_path(graph, edge_cost)?.map(|path| path.total_cost()))
145}
146
147fn snapshot_weights<G, Cost, F>(graph: &G, edge_cost: &mut F) -> Result<Vec<Option<Cost>>>
148where
149 G: IndexGraphView,
150 Cost: Measure,
151 F: FnMut(G::Edge) -> Option<Cost>,
152{
153 let mut weights = vec![None; graph.edge_bound()];
154 for edge in graph.edge_indices() {
155 let Some(weight) = edge_cost(edge) else {
156 continue;
157 };
158 if !weight.is_valid() {
159 return Err(GraphError::InvalidAlgorithmParameter {
160 algorithm: "DAG longest path",
161 parameter: "edge_cost",
162 value: String::from("must be finite"),
163 });
164 }
165 weights[G::edge_slot(edge)] = Some(weight);
166 }
167 Ok(weights)
168}
169
170fn reconstruct<G>(mut node: G::Node, predecessors: &[Option<G::Node>]) -> Vec<G::Node>
171where
172 G: IndexGraphView,
173{
174 let mut path = vec![node];
175 while let Some(parent) = predecessors[G::node_slot(node)] {
176 node = parent;
177 path.push(node);
178 }
179 path.reverse();
180 path
181}