1use crate::registry::{CreateSpec, ProblemSchemaEntry, VariantDimension};
7use crate::topology::{is_simple_st_path, Graph, SimpleGraph};
8use crate::traits::Problem;
9use crate::types::{Max, One, WeightElement};
10use num_traits::Zero;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "LongestPath",
16 display_name: "Longest Path",
17 aliases: &[],
18 dimensions: &[
19 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
20 VariantDimension::new("weight", "i64", &["i64", "One"]),
21 ],
22 category: crate::registry::ProblemCategory::Graph,
23 module_path: module_path!(),
24 description: "Find a simple s-t path of maximum total edge length",
25 fields: LongestPathI64CreateSpec::FIELDS,
26 }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
44#[serde(try_from = "LongestPathData<G, W>")]
45#[serde(bound(deserialize = "G: Graph + Deserialize<'de>, W: WeightElement + Deserialize<'de>"))]
46pub struct LongestPath<G, W: WeightElement> {
47 graph: G,
48 edge_lengths: Vec<W>,
49 source_vertex: usize,
50 target_vertex: usize,
51}
52
53#[derive(Deserialize)]
54#[serde(bound(deserialize = "G: Graph + Deserialize<'de>, W: WeightElement + Deserialize<'de>"))]
55struct LongestPathData<G, W: WeightElement> {
56 graph: G,
57 edge_lengths: Vec<W>,
58 source_vertex: usize,
59 target_vertex: usize,
60}
61
62impl<G, W> TryFrom<LongestPathData<G, W>> for LongestPath<G, W>
63where
64 G: Graph,
65 W: WeightElement,
66{
67 type Error = crate::registry::ConstructionError;
68
69 fn try_from(data: LongestPathData<G, W>) -> Result<Self, Self::Error> {
70 Self::try_new(
71 data.graph,
72 data.edge_lengths,
73 data.source_vertex,
74 data.target_vertex,
75 )
76 }
77}
78
79macro_rules! longest_path_create_spec {
80 (@lengths $spec:ident, $lengths:ident) => { $spec.$lengths };
81 (@lengths $spec:ident) => { vec![One; $spec.graph.len()] };
82 ($name:ident,$weight:ty $(, $lengths:ident)?) => {
83 #[derive(Debug, Deserialize, crate::CreateSpec)]
84 struct $name {
85 #[create(codec = "edge-list")]
86 graph: Vec<(usize, usize)>,
87 num_vertices: Option<usize>,
88 $(#[create(codec = "comma-separated")]
89 $lengths: Vec<$weight>,)?
90 source_vertex: usize,
91 target_vertex: usize,
92 }
93 impl TryFrom<$name> for LongestPath<SimpleGraph, $weight> {
94 type Error = crate::registry::ConstructionError;
95 fn try_from(spec: $name) -> Result<Self, crate::registry::ConstructionError> {
96 if spec.graph.is_empty() && spec.num_vertices.is_none() {
97 return Err("num_vertices is required for an empty graph".into());
98 }
99 for &(u, v) in &spec.graph {
100 if u == v {
101 return Err("self-loops are not allowed".into());
102 }
103 }
104 let inferred = spec
105 .graph
106 .iter()
107 .flat_map(|&(u, v)| [u, v])
108 .max()
109 .map(|v| v.checked_add(1).ok_or("vertex count overflows usize"))
110 .transpose()?
111 .unwrap_or(0);
112 let count = spec.num_vertices.unwrap_or(inferred);
113 if count < inferred {
114 return Err("num_vertices is too small".into());
115 }
116 let edge_lengths = longest_path_create_spec!(@lengths spec $(, $lengths)?);
117 Self::try_new(
118 SimpleGraph::new(count, spec.graph),
119 edge_lengths,
120 spec.source_vertex,
121 spec.target_vertex,
122 )
123 }
124 }
125 };
126}
127longest_path_create_spec!(LongestPathI64CreateSpec, i64, edge_lengths);
128longest_path_create_spec!(LongestPathOneCreateSpec, One);
129
130impl<G: Graph, W: WeightElement> LongestPath<G, W> {
131 pub fn new(graph: G, edge_lengths: Vec<W>, source_vertex: usize, target_vertex: usize) -> Self {
133 Self::try_new(graph, edge_lengths, source_vertex, target_vertex)
134 .unwrap_or_else(|error| panic!("{error}"))
135 }
136
137 fn try_new(
138 graph: G,
139 edge_lengths: Vec<W>,
140 source_vertex: usize,
141 target_vertex: usize,
142 ) -> Result<Self, crate::registry::ConstructionError> {
143 Self::check_weights(&graph, &edge_lengths)?;
144 if source_vertex >= graph.num_vertices() {
145 return Err(format!(
146 "source_vertex {} out of bounds (graph has {} vertices)",
147 source_vertex,
148 graph.num_vertices()
149 )
150 .into());
151 }
152 if target_vertex >= graph.num_vertices() {
153 return Err(format!(
154 "target_vertex {} out of bounds (graph has {} vertices)",
155 target_vertex,
156 graph.num_vertices()
157 )
158 .into());
159 }
160 Ok(Self {
161 graph,
162 edge_lengths,
163 source_vertex,
164 target_vertex,
165 })
166 }
167
168 pub fn graph(&self) -> &G {
170 &self.graph
171 }
172
173 pub fn edge_lengths(&self) -> &[W] {
175 &self.edge_lengths
176 }
177
178 pub fn set_lengths(&mut self, edge_lengths: Vec<W>) {
180 Self::check_weights(&self.graph, &edge_lengths).unwrap_or_else(|error| panic!("{error}"));
181 self.edge_lengths = edge_lengths;
182 }
183
184 fn check_weights(graph: &G, weights: &[W]) -> Result<(), crate::registry::ConstructionError> {
185 if weights.len() != graph.num_edges() {
186 return Err(crate::registry::ConstructionError::length_mismatch(
187 "weights",
188 weights.len(),
189 graph.num_edges(),
190 ));
191 }
192 if !weights
193 .iter()
194 .all(|weight| weight.to_sum() > W::Sum::zero())
195 {
196 return Err("All edge lengths must be positive (> 0)".into());
197 }
198 Ok(())
199 }
200
201 pub fn source_vertex(&self) -> usize {
203 self.source_vertex
204 }
205
206 pub fn target_vertex(&self) -> usize {
208 self.target_vertex
209 }
210
211 pub fn is_weighted(&self) -> bool {
213 !W::IS_UNIT
214 }
215
216 pub fn num_vertices(&self) -> usize {
218 self.graph.num_vertices()
219 }
220
221 pub fn num_edges(&self) -> usize {
223 self.graph.num_edges()
224 }
225
226 pub fn is_valid_solution(&self, config: &[bool]) -> bool {
228 is_simple_st_path(
229 self.graph.num_vertices(),
230 &self.graph.edges(),
231 self.source_vertex,
232 self.target_vertex,
233 config,
234 )
235 }
236}
237
238impl<G, W> Problem for LongestPath<G, W>
239where
240 G: Graph + crate::variant::VariantParam,
241 W: WeightElement + crate::variant::VariantParam,
242{
243 const NAME: &'static str = "LongestPath";
244 type Solution = Vec<bool>;
245 type Value = Max<W::Sum>;
246
247 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
248
249 fn variant() -> Vec<(&'static str, &'static str)> {
250 crate::variant_params![G, W]
251 }
252
253 fn evaluate(
254 &self,
255 config: &Self::Solution,
256 ) -> Result<Max<W::Sum>, crate::traits::EvaluationError> {
257 if config.len() != self.graph.num_edges() {
258 return Err(crate::traits::EvaluationError::InvalidConfiguration(
259 "edge-selection length does not match the graph".into(),
260 ));
261 }
262 Ok({
263 if !self.is_valid_solution(config) {
264 return Ok(Max(None));
265 }
266
267 let mut total = W::Sum::zero();
268 for (idx, &selected) in config.iter().enumerate() {
269 if selected {
270 total = W::checked_add_to_sum(
271 total,
272 self.edge_lengths[idx].to_sum(),
273 "summing path edge lengths",
274 )?;
275 }
276 }
277 Max(Some(total))
278 })
279 }
280}
281
282impl<G, W> crate::solvers::BruteForceProblem for LongestPath<G, W>
283where
284 G: Graph + crate::variant::VariantParam,
285 W: WeightElement + crate::variant::VariantParam,
286{
287 fn dimensions(&self) -> Vec<usize> {
288 vec![2; self.graph.num_edges()]
289 }
290}
291
292crate::declare_variants! {
293 default LongestPath<SimpleGraph, i64> => "num_vertices * 2^num_vertices" create LongestPathI64CreateSpec,
294 LongestPath<SimpleGraph, One> => "num_vertices * 2^num_vertices" create LongestPathOneCreateSpec,
295}
296
297crate::register_brute_force! {
298 LongestPath<SimpleGraph, i64> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
299 LongestPath<SimpleGraph, One> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
300}
301
302#[cfg(feature = "example-db")]
303pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
304 vec![crate::example_db::specs::ModelExampleSpec {
305 id: "longest_path_simplegraph",
306 instance: Box::new(LongestPath::new(
307 SimpleGraph::new(
308 7,
309 vec![
310 (0, 1),
311 (0, 2),
312 (1, 3),
313 (2, 3),
314 (2, 4),
315 (3, 5),
316 (4, 5),
317 (4, 6),
318 (5, 6),
319 (1, 6),
320 ],
321 ),
322 vec![3, 2, 4, 1, 5, 2, 3, 2, 4, 1],
323 0,
324 6,
325 )),
326 optimal_config: serde_json::json!(vec![
327 true, false, true, true, true, false, true, false, true, false
328 ]),
329 optimal_value: serde_json::json!(20),
330 }]
331}
332
333#[cfg(test)]
334#[path = "../../unit_tests/models/graph/longest_path.rs"]
335mod tests;
336
337crate::decision_problem_meta!(LongestPath<SimpleGraph, One>, "DecisionLongestPath");
338crate::register_decision_variant!(
339 LongestPath<SimpleGraph, One>, "DecisionLongestPath", "num_vertices * 2^num_vertices", &[],
340 "Does a feasible solution have objective value >= the bound?",
341 category: crate::registry::ProblemCategory::Graph,
342 dims: [
343 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
344 VariantDimension::new("weight", "One", &["One"]),
345 ],
346 fields: [
347 crate::registry::FieldInfo { name: "graph", type_name: "Vec<(usize,usize)>", description: "Graph edges as comma-separated vertex pairs." },
348 crate::registry::FieldInfo { name: "num_vertices", type_name: "usize", description: "Number of vertices, including isolated vertices." },
349 crate::registry::FieldInfo { name: "source_vertex", type_name: "usize", description: "Start vertex of the path." },
350 crate::registry::FieldInfo { name: "target_vertex", type_name: "usize", description: "End vertex of the path." },
351 crate::registry::FieldInfo { name: "bound", type_name: "i64", description: "Accept objective values >= this bound" },
352 ],
353 decode: |_, indices: Vec<usize>| crate::config::config_to_bits(&indices)
354);
355
356#[cfg(feature = "example-db")]
357pub(crate) fn decision_canonical_rule_example_specs(
358) -> Vec<crate::example_db::specs::RuleExampleSpec> {
359 vec![crate::example_db::specs::RuleExampleSpec {
360 id: "decision_longest_path_to_longest_path",
361 build: || {
362 let source = crate::models::decision::Decision::new(
363 LongestPath::new(SimpleGraph::path(3), vec![crate::types::One; 2], 0, 2),
364 2,
365 );
366 let witness = serde_json::json!(vec![true, true]);
367 crate::example_db::specs::rule_example_with_witness::<_, LongestPath<SimpleGraph, One>>(
368 source,
369 crate::export::SolutionPair {
370 source_config: witness.clone(),
371 target_config: witness,
372 },
373 )
374 },
375 }]
376}