problemreductions/models/graph/
directed_hamiltonian_path.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
7use crate::topology::DirectedGraph;
8use crate::traits::Problem;
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12 ProblemSchemaEntry {
13 name: "DirectedHamiltonianPath",
14 display_name: "Directed Hamiltonian Path",
15 aliases: &["DHP"],
16 dimensions: &[],
17 category: crate::registry::ProblemCategory::Graph,
18 module_path: module_path!(),
19 description: "Does the directed graph contain a Hamiltonian path?",
20 fields: &[
21 FieldInfo { name: "graph", type_name: "DirectedGraph", description: "The directed graph G=(V,A)" },
22 ],
23 }
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct DirectedHamiltonianPath {
57 graph: DirectedGraph,
58}
59
60impl DirectedHamiltonianPath {
61 pub fn new(graph: DirectedGraph) -> Self {
63 Self { graph }
64 }
65
66 pub fn graph(&self) -> &DirectedGraph {
68 &self.graph
69 }
70
71 pub fn num_vertices(&self) -> usize {
73 self.graph.num_vertices()
74 }
75
76 pub fn num_arcs(&self) -> usize {
78 self.graph.num_arcs()
79 }
80
81 pub fn is_valid_solution(&self, solution: &[usize]) -> bool {
83 is_valid_directed_hamiltonian_path(&self.graph, solution)
84 }
85}
86
87impl Problem for DirectedHamiltonianPath {
88 const NAME: &'static str = "DirectedHamiltonianPath";
89 type Solution = Vec<usize>;
90 type Value = crate::types::Or;
91
92 crate::problem_parameters![("num_arcs", num_arcs), ("num_vertices", num_vertices),];
93
94 fn variant() -> Vec<(&'static str, &'static str)> {
95 crate::variant_params![]
96 }
97
98 fn evaluate(
99 &self,
100 solution: &Self::Solution,
101 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
102 let n = self.graph.num_vertices();
103 if solution.len() != n {
104 return Err(crate::traits::EvaluationError::InvalidConfiguration(
105 "path ordering length does not match the graph vertices".into(),
106 ));
107 }
108 if solution.iter().any(|&vertex| vertex >= n) {
109 return Err(crate::traits::EvaluationError::InvalidConfiguration(
110 "path ordering contains an out-of-range vertex".into(),
111 ));
112 }
113 Ok(crate::types::Or(is_valid_directed_hamiltonian_path(
114 &self.graph,
115 solution,
116 )))
117 }
118}
119
120impl crate::solvers::BruteForceProblem for DirectedHamiltonianPath {
121 fn dimensions(&self) -> Vec<usize> {
122 lehmer_dims(self.graph.num_vertices())
123 }
124}
125
126pub(crate) fn lehmer_dims(n: usize) -> Vec<usize> {
128 (1..=n).rev().collect()
129}
130
131pub(crate) fn decode_lehmer(code: &[usize]) -> Vec<usize> {
136 let n = code.len();
137 let mut available: Vec<usize> = (0..n).collect();
138 let mut perm = Vec::with_capacity(n);
139 for &idx in code {
140 let idx = idx.min(available.len().saturating_sub(1));
141 perm.push(available.remove(idx));
142 }
143 perm
144}
145
146pub(crate) fn is_valid_directed_hamiltonian_path(graph: &DirectedGraph, perm: &[usize]) -> bool {
151 let n = graph.num_vertices();
152 if perm.len() != n {
153 return false;
154 }
155
156 let mut seen = vec![false; n];
158 for &v in perm {
159 if v >= n || seen[v] {
160 return false;
161 }
162 seen[v] = true;
163 }
164
165 for i in 0..n.saturating_sub(1) {
167 if !graph.has_arc(perm[i], perm[i + 1]) {
168 return false;
169 }
170 }
171
172 true
173}
174
175#[cfg(feature = "example-db")]
176pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
177 let graph = DirectedGraph::new(
180 6,
181 vec![
182 (0, 1),
183 (0, 3),
184 (1, 3),
185 (1, 4),
186 (2, 0),
187 (2, 4),
188 (3, 2),
189 (3, 5),
190 (4, 5),
191 (5, 1),
192 ],
193 );
194 let optimal_perm = vec![0usize, 1, 3, 2, 4, 5];
195 vec![crate::example_db::specs::ModelExampleSpec {
196 id: "directed_hamiltonian_path",
197 instance: Box::new(DirectedHamiltonianPath::new(graph)),
198 optimal_config: serde_json::to_value(optimal_perm)
199 .expect("solution serialization must succeed"),
200 optimal_value: serde_json::json!(true),
201 }]
202}
203
204crate::declare_variants! {
205 default DirectedHamiltonianPath => "num_vertices^2 * 2^num_vertices",
206}
207
208crate::register_brute_force! {
209 DirectedHamiltonianPath decode |_problem: &DirectedHamiltonianPath, indices: Vec<usize>| decode_lehmer(&indices),
210}
211
212#[cfg(test)]
213#[path = "../../unit_tests/models/graph/directed_hamiltonian_path.rs"]
214mod tests;