Skip to main content

problemreductions/models/graph/
hamiltonian_circuit.rs

1//! Hamiltonian Circuit problem implementation.
2//!
3//! The Hamiltonian Circuit problem asks whether a graph contains a cycle
4//! that visits every vertex exactly once and returns to the starting vertex.
5
6use 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: "HamiltonianCircuit",
15        display_name: "Hamiltonian Circuit",
16        aliases: &["HC"],
17        dimensions: &[
18            VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
19        ],
20        category: crate::registry::ProblemCategory::Graph,
21        module_path: module_path!(),
22        description: "Does the graph contain a Hamiltonian circuit?",
23        fields: &[
24            FieldInfo { name: "graph", type_name: "G", description: "The undirected graph G=(V,E)" },
25        ],
26    }
27}
28
29/// The Hamiltonian Circuit problem.
30///
31/// Given a graph G = (V, E), determine whether there exists a cycle that
32/// visits every vertex exactly once and returns to the starting vertex.
33///
34/// # Type Parameters
35///
36/// * `G` - Graph type (e.g., SimpleGraph)
37///
38/// # Example
39///
40/// ```
41/// use problemreductions::models::graph::HamiltonianCircuit;
42/// use problemreductions::topology::SimpleGraph;
43/// use problemreductions::{Problem, BruteForce};
44///
45/// // Square graph (4-cycle) has a Hamiltonian circuit
46/// let graph = SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3), (0, 3)]);
47/// let problem = HamiltonianCircuit::new(graph);
48///
49/// let solver = BruteForce::new();
50/// let solutions = solver.find_all_witnesses(&problem).unwrap();
51///
52/// // Verify all solutions are valid Hamiltonian circuits
53/// for sol in &solutions {
54///     assert!(problem.evaluate(sol).unwrap());
55/// }
56/// ```
57#[derive(Debug, Clone, Serialize, Deserialize)]
58#[serde(bound(deserialize = "G: serde::Deserialize<'de>"))]
59pub struct HamiltonianCircuit<G> {
60    /// The underlying graph.
61    graph: G,
62}
63
64impl<G: Graph> HamiltonianCircuit<G> {
65    /// Create a new Hamiltonian Circuit problem from a graph.
66    pub fn new(graph: G) -> Self {
67        Self { graph }
68    }
69
70    /// Get a reference to the underlying graph.
71    pub fn graph(&self) -> &G {
72        &self.graph
73    }
74
75    /// Get the number of vertices in the underlying graph.
76    pub fn num_vertices(&self) -> usize {
77        self.graph().num_vertices()
78    }
79
80    /// Get the number of edges in the underlying graph.
81    pub fn num_edges(&self) -> usize {
82        self.graph().num_edges()
83    }
84
85    /// Check if a configuration is a valid Hamiltonian circuit.
86    pub fn is_valid_solution(&self, config: &[usize]) -> bool {
87        is_valid_hamiltonian_circuit(&self.graph, config)
88    }
89}
90
91impl<G> Problem for HamiltonianCircuit<G>
92where
93    G: Graph + VariantParam,
94{
95    const NAME: &'static str = "HamiltonianCircuit";
96    type Solution = Vec<usize>;
97    type Value = crate::types::Or;
98
99    crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
100
101    fn variant() -> Vec<(&'static str, &'static str)> {
102        crate::variant_params![G]
103    }
104
105    fn evaluate(
106        &self,
107        config: &Self::Solution,
108    ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
109        let n = self.graph.num_vertices();
110        if config.len() != n {
111            return Err(crate::traits::EvaluationError::InvalidConfiguration(
112                "circuit ordering length does not match the graph vertices".into(),
113            ));
114        }
115        if config.iter().any(|&vertex| vertex >= n) {
116            return Err(crate::traits::EvaluationError::InvalidConfiguration(
117                "circuit ordering contains an out-of-range vertex".into(),
118            ));
119        }
120        Ok(crate::types::Or(is_valid_hamiltonian_circuit(
121            &self.graph,
122            config,
123        )))
124    }
125}
126
127impl<G> crate::solvers::BruteForceProblem for HamiltonianCircuit<G>
128where
129    G: Graph + VariantParam,
130{
131    fn dimensions(&self) -> Vec<usize> {
132        let n = self.graph.num_vertices();
133        vec![n; n]
134    }
135}
136
137/// Check if a configuration represents a valid Hamiltonian circuit in the graph.
138///
139/// A valid Hamiltonian circuit is a permutation of the vertices such that
140/// consecutive vertices in the permutation are adjacent in the graph,
141/// including a closing edge from the last vertex back to the first.
142pub(crate) fn is_valid_hamiltonian_circuit<G: Graph>(graph: &G, config: &[usize]) -> bool {
143    let n = graph.num_vertices();
144    if n < 3 || config.len() != n {
145        return false;
146    }
147
148    // Check that config is a valid permutation of 0..n
149    let mut seen = vec![false; n];
150    for &v in config {
151        if v >= n || seen[v] {
152            return false;
153        }
154        seen[v] = true;
155    }
156
157    // Check that consecutive vertices (including wrap-around) are connected by edges
158    for i in 0..n {
159        let u = config[i];
160        let v = config[(i + 1) % n];
161        if !graph.has_edge(u, v) {
162            return false;
163        }
164    }
165
166    true
167}
168
169#[cfg(feature = "example-db")]
170pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
171    vec![crate::example_db::specs::ModelExampleSpec {
172        id: "hamiltonian_circuit_simplegraph",
173        // Prism graph (triangular prism): 6 vertices, 9 edges
174        instance: Box::new(HamiltonianCircuit::new(SimpleGraph::new(
175            6,
176            vec![
177                (0, 1),
178                (1, 2),
179                (2, 0),
180                (3, 4),
181                (4, 5),
182                (5, 3),
183                (0, 3),
184                (1, 4),
185                (2, 5),
186            ],
187        ))),
188        optimal_config: serde_json::json!(vec![0, 1, 2, 5, 4, 3]),
189        optimal_value: serde_json::json!(true),
190    }]
191}
192
193crate::impl_random_generate!(
194    HamiltonianCircuit<SimpleGraph>,
195    crate::random::SimpleGraphRandomSpec,
196    |spec| { Ok(HamiltonianCircuit::new(spec.graph()?)) }
197);
198
199crate::declare_variants! {
200    default HamiltonianCircuit<SimpleGraph> => "1.657^num_vertices" random,
201}
202
203crate::register_brute_force! {
204    HamiltonianCircuit<SimpleGraph>,
205}
206
207#[cfg(test)]
208#[path = "../../unit_tests/models/graph/hamiltonian_circuit.rs"]
209mod tests;