problemreductions/rules/
hamiltoniancircuit_stackercrane.rs1use crate::models::graph::HamiltonianCircuit;
16use crate::models::misc::StackerCrane;
17use crate::reduction;
18use crate::rules::traits::{ReduceTo, ReductionResult};
19use crate::topology::{Graph, SimpleGraph};
20
21#[derive(Debug, Clone)]
23pub struct ReductionHamiltonianCircuitToStackerCrane {
24 target: StackerCrane,
25}
26
27impl ReductionResult for ReductionHamiltonianCircuitToStackerCrane {
28 type Source = HamiltonianCircuit<SimpleGraph>;
29 type Target = StackerCrane;
30
31 fn target_problem(&self) -> &Self::Target {
32 &self.target
33 }
34
35 fn extract_solution(
36 &self,
37 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
38 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
39 let value =
40 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
41 if !crate::rules::AggregateReductionResult::extract_value(self, value).0 {
42 return Err(crate::rules::ExtractionError::invalid(
43 "target tour does not certify a Hamiltonian circuit",
44 ));
45 }
46 Ok(target_solution.to_vec())
48 }
49}
50
51impl crate::rules::AggregateReductionResult for ReductionHamiltonianCircuitToStackerCrane {
52 type Source = HamiltonianCircuit<SimpleGraph>;
53 type Target = StackerCrane;
54
55 fn target_problem(&self) -> &Self::Target {
56 &self.target
57 }
58
59 fn extract_value(&self, value: crate::types::Min<i64>) -> crate::types::Or {
60 crate::types::Or(
61 self.target.num_arcs() >= 3
62 && value
63 .0
64 .is_some_and(|cost| usize::try_from(cost) == Ok(self.target.num_vertices())),
65 )
66 }
67}
68
69#[reduction(
70 aggregate = custom,
71 transform = exact {
72 num_vertices = "2 * num_vertices",
73 num_arcs = "num_vertices",
74 num_edges = "2 * num_edges",
75 }
76)]
77impl ReduceTo<StackerCrane> for HamiltonianCircuit<SimpleGraph> {
78 type Result = ReductionHamiltonianCircuitToStackerCrane;
79
80 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
81 let n = self.num_vertices();
82
83 let (target_num_vertices, target_num_edges) = split_graph_dimensions(n, self.num_edges())?;
85
86 let arcs: Vec<(usize, usize)> = (0..n).map(|i| (2 * i, 2 * i + 1)).collect();
88 let arc_lengths: Vec<i64> = vec![1; n];
89
90 let mut edges = Vec::with_capacity(target_num_edges);
96 let mut edge_lengths = Vec::with_capacity(target_num_edges);
97 for (u, v) in self.graph().edges() {
98 edges.push((2 * u + 1, 2 * v));
99 edge_lengths.push(1);
100 edges.push((2 * v + 1, 2 * u));
101 edge_lengths.push(1);
102 }
103
104 let target =
105 StackerCrane::try_new(target_num_vertices, arcs, edges, arc_lengths, edge_lengths)
106 .map_err(<Self as ReduceTo<StackerCrane>>::target_construction)?;
107
108 Ok(ReductionHamiltonianCircuitToStackerCrane { target })
109 }
110}
111
112fn split_graph_dimensions(
114 n: usize,
115 m: usize,
116) -> Result<(usize, usize), crate::rules::ReductionError> {
117 type Source = HamiltonianCircuit<SimpleGraph>;
118 let overflow = || {
119 crate::rules::ReductionError::integer_overflow::<Source, StackerCrane>(
120 "encoding split graph dimensions and route costs",
121 )
122 };
123 let vertices = n.checked_mul(2).ok_or_else(overflow)?;
124 let edges = m.checked_mul(2).ok_or_else(overflow)?;
125 let cost_bound = n.checked_mul(vertices).ok_or_else(overflow)?;
128 <Source as ReduceTo<StackerCrane>>::exact_i64(cost_bound, "bounding split graph route costs")?;
129 Ok((vertices, edges))
130}
131
132#[cfg(feature = "example-db")]
133pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
134 use crate::export::SolutionPair;
135
136 vec![crate::example_db::specs::RuleExampleSpec {
137 id: "hamiltoniancircuit_to_stackercrane",
138 build: || {
139 let source = HamiltonianCircuit::new(SimpleGraph::cycle(4));
140 crate::example_db::specs::rule_example_with_witness::<_, StackerCrane>(
141 source,
142 SolutionPair {
143 source_config: serde_json::json!(vec![0, 1, 2, 3]),
144 target_config: serde_json::json!(vec![0, 1, 2, 3]),
145 },
146 )
147 },
148 }]
149}
150
151#[cfg(test)]
152#[path = "../unit_tests/rules/hamiltoniancircuit_stackercrane.rs"]
153mod tests;