problemreductions/models/graph/
directed_hamiltonian_path.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
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 VariantDimension::new("graph", "DirectedGraph", &["DirectedGraph"]),
18 ],
19 module_path: module_path!(),
20 description: "Does the directed graph contain a Hamiltonian path?",
21 fields: &[
22 FieldInfo { name: "graph", type_name: "DirectedGraph", description: "The directed graph G=(V,A)" },
23 ],
24 }
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct DirectedHamiltonianPath {
58 graph: DirectedGraph,
59}
60
61impl DirectedHamiltonianPath {
62 pub fn new(graph: DirectedGraph) -> Self {
64 Self { graph }
65 }
66
67 pub fn graph(&self) -> &DirectedGraph {
69 &self.graph
70 }
71
72 pub fn num_vertices(&self) -> usize {
74 self.graph.num_vertices()
75 }
76
77 pub fn num_arcs(&self) -> usize {
79 self.graph.num_arcs()
80 }
81
82 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
84 let perm = decode_lehmer(config);
85 is_valid_directed_hamiltonian_path(&self.graph, &perm)
86 }
87}
88
89impl Problem for DirectedHamiltonianPath {
90 const NAME: &'static str = "DirectedHamiltonianPath";
91 type Value = crate::types::Or;
92
93 fn variant() -> Vec<(&'static str, &'static str)> {
94 crate::variant_params![]
95 }
96
97 fn dims(&self) -> Vec<usize> {
98 lehmer_dims(self.graph.num_vertices())
99 }
100
101 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
102 let perm = decode_lehmer(config);
103 crate::types::Or(is_valid_directed_hamiltonian_path(&self.graph, &perm))
104 }
105}
106
107pub(crate) fn lehmer_dims(n: usize) -> Vec<usize> {
109 (1..=n).rev().collect()
110}
111
112pub(crate) fn decode_lehmer(code: &[usize]) -> Vec<usize> {
117 let n = code.len();
118 let mut available: Vec<usize> = (0..n).collect();
119 let mut perm = Vec::with_capacity(n);
120 for &idx in code {
121 let idx = idx.min(available.len().saturating_sub(1));
122 perm.push(available.remove(idx));
123 }
124 perm
125}
126
127pub(crate) fn is_valid_directed_hamiltonian_path(graph: &DirectedGraph, perm: &[usize]) -> bool {
132 let n = graph.num_vertices();
133 if perm.len() != n {
134 return false;
135 }
136
137 let mut seen = vec![false; n];
139 for &v in perm {
140 if v >= n || seen[v] {
141 return false;
142 }
143 seen[v] = true;
144 }
145
146 for i in 0..n.saturating_sub(1) {
148 if !graph.has_arc(perm[i], perm[i + 1]) {
149 return false;
150 }
151 }
152
153 true
154}
155
156#[cfg(feature = "example-db")]
157pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
158 use crate::rules::ilp_helpers::permutation_to_lehmer;
159
160 let graph = DirectedGraph::new(
163 6,
164 vec![
165 (0, 1),
166 (0, 3),
167 (1, 3),
168 (1, 4),
169 (2, 0),
170 (2, 4),
171 (3, 2),
172 (3, 5),
173 (4, 5),
174 (5, 1),
175 ],
176 );
177 let optimal_perm = vec![0usize, 1, 3, 2, 4, 5];
178 let optimal_config = permutation_to_lehmer(&optimal_perm);
179 vec![crate::example_db::specs::ModelExampleSpec {
180 id: "directed_hamiltonian_path",
181 instance: Box::new(DirectedHamiltonianPath::new(graph)),
182 optimal_config,
183 optimal_value: serde_json::json!(true),
184 }]
185}
186
187crate::declare_variants! {
188 default DirectedHamiltonianPath => "num_vertices^2 * 2^num_vertices",
189}
190
191#[cfg(test)]
192#[path = "../../unit_tests/models/graph/directed_hamiltonian_path.rs"]
193mod tests;