problemreductions/models/graph/
hamiltonian_path.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
7use crate::topology::{Graph, SimpleGraph};
8use crate::traits::Problem;
9use crate::variant::VariantParam;
10use serde::{Deserialize, Serialize};
11
12inventory::submit! {
13 ProblemSchemaEntry {
14 name: "HamiltonianPath",
15 display_name: "Hamiltonian Path",
16 aliases: &[],
17 dimensions: &[
18 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
19 ],
20 category: crate::registry::ProblemCategory::Graph,
21 module_path: module_path!(),
22 description: "Find a Hamiltonian path in a graph",
23 fields: &[
24 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
25 ],
26 }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
66#[serde(bound(deserialize = "G: serde::Deserialize<'de>"))]
67pub struct HamiltonianPath<G> {
68 graph: G,
69}
70
71impl<G: Graph> HamiltonianPath<G> {
72 pub fn new(graph: G) -> Self {
74 Self { graph }
75 }
76
77 pub fn graph(&self) -> &G {
79 &self.graph
80 }
81
82 pub fn num_vertices(&self) -> usize {
84 self.graph.num_vertices()
85 }
86
87 pub fn num_edges(&self) -> usize {
89 self.graph.num_edges()
90 }
91
92 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
94 is_valid_hamiltonian_path(&self.graph, config)
95 }
96}
97
98impl<G> Problem for HamiltonianPath<G>
99where
100 G: Graph + VariantParam,
101{
102 const NAME: &'static str = "HamiltonianPath";
103 type Solution = Vec<usize>;
104 type Value = crate::types::Or;
105
106 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
107
108 fn variant() -> Vec<(&'static str, &'static str)> {
109 crate::variant_params![G]
110 }
111
112 fn evaluate(
113 &self,
114 config: &Self::Solution,
115 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
116 let n = self.graph.num_vertices();
117 if config.len() != n {
118 return Err(crate::traits::EvaluationError::InvalidConfiguration(
119 "path ordering length does not match the graph vertices".into(),
120 ));
121 }
122 if config.iter().any(|&vertex| vertex >= n) {
123 return Err(crate::traits::EvaluationError::InvalidConfiguration(
124 "path ordering contains an out-of-range vertex".into(),
125 ));
126 }
127 Ok(crate::types::Or(is_valid_hamiltonian_path(
128 &self.graph,
129 config,
130 )))
131 }
132}
133
134impl<G> crate::solvers::BruteForceProblem for HamiltonianPath<G>
135where
136 G: Graph + VariantParam,
137{
138 fn dimensions(&self) -> Vec<usize> {
139 let n = self.graph.num_vertices();
140 vec![n; n]
141 }
142}
143
144pub(crate) fn is_valid_hamiltonian_path<G: Graph>(graph: &G, config: &[usize]) -> bool {
149 let n = graph.num_vertices();
150 if config.len() != n {
151 return false;
152 }
153
154 let mut seen = vec![false; n];
156 for &v in config {
157 if v >= n || seen[v] {
158 return false;
159 }
160 seen[v] = true;
161 }
162
163 for i in 0..n.saturating_sub(1) {
165 if !graph.has_edge(config[i], config[i + 1]) {
166 return false;
167 }
168 }
169
170 true
171}
172
173#[cfg(feature = "example-db")]
174pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
175 vec![crate::example_db::specs::ModelExampleSpec {
176 id: "hamiltonian_path_simplegraph",
177 instance: Box::new(HamiltonianPath::new(SimpleGraph::new(
178 6,
179 vec![
180 (0, 1),
181 (0, 2),
182 (1, 3),
183 (2, 3),
184 (3, 4),
185 (3, 5),
186 (4, 2),
187 (5, 1),
188 ],
189 ))),
190 optimal_config: serde_json::json!(vec![0, 2, 4, 3, 1, 5]),
191 optimal_value: serde_json::json!(true),
192 }]
193}
194
195crate::impl_random_generate!(
197 HamiltonianPath<SimpleGraph>,
198 crate::random::SimpleGraphRandomSpec,
199 |spec| { Ok(HamiltonianPath::new(spec.graph()?)) }
200);
201
202crate::declare_variants! {
203 default HamiltonianPath<SimpleGraph> => "1.657^num_vertices" random,
204}
205
206crate::register_brute_force! {
207 HamiltonianPath<SimpleGraph>,
208}
209
210#[cfg(test)]
211#[path = "../../unit_tests/models/graph/hamiltonian_path.rs"]
212mod tests;