problemreductions/rules/
hamiltoniancircuit_ruralpostman.rs1use crate::models::decision::Decision;
27use crate::models::graph::{HamiltonianCircuit, RuralPostman};
28use crate::reduction;
29use crate::rules::traits::{ReduceTo, ReductionResult};
30use crate::topology::{Graph, SimpleGraph};
31
32#[derive(Debug, Clone)]
34pub struct ReductionHamiltonianCircuitToRuralPostman {
35 target: Decision<RuralPostman<SimpleGraph, i64>>,
36 n: usize,
38 source_edges: Vec<(usize, usize)>,
40}
41
42impl ReductionResult for ReductionHamiltonianCircuitToRuralPostman {
43 type Source = HamiltonianCircuit<SimpleGraph>;
44 type Target = Decision<RuralPostman<SimpleGraph, i64>>;
45
46 fn target_problem(&self) -> &Self::Target {
47 &self.target
48 }
49
50 fn extract_solution(
51 &self,
52 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
53 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
54 crate::rules::traits::validate_target_witness(
55 self.target_problem(),
56 target_solution,
57 |value| value.0,
58 "target witness does not certify a YES answer for the source",
59 )?;
60
61 Ok({
62 let n = self.n;
73
74 let mut successor = vec![usize::MAX; n];
76 for (k, &(vi, vj)) in self.source_edges.iter().enumerate() {
77 let fwd_idx = n + 2 * k; let bwd_idx = n + 2 * k + 1; let fwd_mult = target_solution[fwd_idx];
81 let bwd_mult = target_solution[bwd_idx];
82
83 if fwd_mult > 0 && successor[vi] == usize::MAX {
86 successor[vi] = vj;
87 }
88 if bwd_mult > 0 && successor[vj] == usize::MAX {
89 successor[vj] = vi;
90 }
91 }
92
93 let mut cycle = Vec::with_capacity(n);
95 let mut current = 0;
96 for _ in 0..n {
97 cycle.push(current);
98 let next = successor[current];
99 if next == usize::MAX {
100 return Err(crate::rules::ExtractionError::invalid(
101 "target tour does not provide one successor for every source vertex",
102 ));
103 }
104 current = next;
105 }
106
107 cycle
108 })
109 }
110}
111
112#[crate::aggregate_reduction(identity)]
113impl crate::rules::AggregateReductionResult for ReductionHamiltonianCircuitToRuralPostman {}
114
115#[reduction(
116 transform = exact {
117 num_vertices = "2 * num_vertices",
118 num_edges = "num_vertices + 2 * num_edges",
119 num_required_edges = "num_vertices",
120 }
121)]
122impl ReduceTo<Decision<RuralPostman<SimpleGraph, i64>>> for HamiltonianCircuit<SimpleGraph> {
123 type Result = ReductionHamiltonianCircuitToRuralPostman;
124
125 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
126 let n = self.num_vertices();
127 let source_edges: Vec<(usize, usize)> = self.graph().edges();
128 let m = source_edges.len();
129
130 let num_target_edges = n + 2 * m;
132 let mut target_edges = Vec::with_capacity(num_target_edges);
133 let mut edge_weights = Vec::with_capacity(num_target_edges);
134 let mut required_edges = Vec::with_capacity(n);
135
136 for i in 0..n {
138 target_edges.push((2 * i, 2 * i + 1));
139 edge_weights.push(1);
140 required_edges.push(i); }
142
143 for &(vi, vj) in &source_edges {
147 target_edges.push((2 * vi + 1, 2 * vj));
148 edge_weights.push(1);
149 target_edges.push((2 * vj + 1, 2 * vi));
150 edge_weights.push(1);
151 }
152
153 let target_graph = SimpleGraph::new(2 * n, target_edges);
154 let target = RuralPostman::new(target_graph, edge_weights, required_edges);
155
156 Ok(ReductionHamiltonianCircuitToRuralPostman {
157 target: Decision::new(
158 target,
159 if n < 3 {
160 -1
161 } else {
162 <Self as ReduceTo<Decision<RuralPostman<SimpleGraph, i64>>>>::exact_i64(
163 2 * n,
164 "encoding the route bound",
165 )?
166 },
167 ),
168 n,
169 source_edges,
170 })
171 }
172}
173
174#[cfg(feature = "example-db")]
175pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
176 use crate::export::SolutionPair;
177
178 vec![crate::example_db::specs::RuleExampleSpec {
179 id: "hamiltoniancircuit_to_ruralpostman",
180 build: || {
181 let source = HamiltonianCircuit::new(SimpleGraph::new(3, vec![(0, 1), (1, 2), (0, 2)]));
183
184 crate::example_db::specs::rule_example_with_witness::<
192 _,
193 Decision<RuralPostman<SimpleGraph, i64>>,
194 >(
195 source,
196 SolutionPair {
197 source_config: serde_json::json!(vec![0, 1, 2]),
198 target_config: serde_json::json!(vec![1, 1, 1, 1, 0, 1, 0, 0, 1]),
199 },
200 )
201 },
202 }]
203}
204
205#[cfg(test)]
206#[path = "../unit_tests/rules/hamiltoniancircuit_ruralpostman.rs"]
207mod tests;