problemreductions/rules/
prizecollectingsteinerforest_steinertree.rs1use crate::models::graph::{PrizeCollectingSteinerForest, SteinerTree};
35use crate::reduction;
36use crate::rules::traits::{ReduceTo, ReductionResult};
37use crate::topology::{Graph, SimpleGraph};
38
39#[derive(Debug, Clone)]
47pub struct ReductionPCSFToSteinerTree {
48 target: SteinerTree<SimpleGraph, i32>,
49 num_source_vertices: usize,
52 num_source_edges: usize,
55 target_to_source_edge: Vec<Option<usize>>,
58 target_to_include_vertex: Vec<Option<usize>>,
62}
63
64impl ReductionResult for ReductionPCSFToSteinerTree {
65 type Source = PrizeCollectingSteinerForest<SimpleGraph, i32>;
66 type Target = SteinerTree<SimpleGraph, i32>;
67
68 fn target_problem(&self) -> &SteinerTree<SimpleGraph, i32> {
69 &self.target
70 }
71
72 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
73 let n = self.num_source_vertices;
74 let m = self.num_source_edges;
75 let mut source_config = vec![0usize; n + m];
76
77 for (target_idx, &selected) in target_solution.iter().enumerate() {
80 if selected != 1 {
81 continue;
82 }
83 if let Some(v) = self.target_to_include_vertex[target_idx] {
84 source_config[v] = 1;
85 } else if let Some(src_edge) = self.target_to_source_edge[target_idx] {
86 source_config[n + src_edge] = 1;
87 }
88 }
89
90 let edges = self.target.graph().edges();
95 for (target_idx, &(_, _)) in edges.iter().enumerate() {
96 if target_solution.get(target_idx).copied() != Some(1) {
97 continue;
98 }
99 if let Some(src_edge) = self.target_to_source_edge[target_idx] {
100 let (u, v) = self.source_edge_pair(src_edge);
101 source_config[u] = 1;
102 source_config[v] = 1;
103 }
104 }
105
106 source_config
107 }
108}
109
110impl ReductionPCSFToSteinerTree {
111 fn source_edge_pair(&self, src_edge_idx: usize) -> (usize, usize) {
114 self.target.graph().edges()[src_edge_idx]
115 }
116}
117
118#[reduction(
119 overhead = {
120 num_vertices = "num_vertices + num_vertices_with_prize + 1",
121 num_edges = "num_edges + num_vertices + 2 * num_vertices_with_prize",
122 num_terminals = "num_vertices_with_prize + 1",
123 }
124)]
125impl ReduceTo<SteinerTree<SimpleGraph, i32>> for PrizeCollectingSteinerForest<SimpleGraph, i32> {
126 type Result = ReductionPCSFToSteinerTree;
127
128 fn reduce_to(&self) -> Self::Result {
129 let n = self.num_vertices();
130 let m = self.num_edges();
131 let source_edges = self.graph().edges();
132 let source_edge_costs = self.edge_costs();
133 let source_prizes = self.vertex_prizes();
134 let beta = *self.beta();
135 let omega = *self.omega();
136
137 let prized: Vec<usize> = (0..n).filter(|&v| source_prizes[v] > 0).collect();
143 let k = prized.len();
144 let root = n;
145 let gadget_terminal = |gadget_pos: usize| -> usize { n + 1 + gadget_pos };
146
147 let target_num_vertices = n + 1 + k;
148 let mut target_edges: Vec<(usize, usize)> = Vec::with_capacity(m + n + 2 * k);
149 let mut target_edge_weights: Vec<i32> = Vec::with_capacity(m + n + 2 * k);
150 let mut target_to_source_edge: Vec<Option<usize>> = Vec::with_capacity(m + n + 2 * k);
151 let mut target_to_include_vertex: Vec<Option<usize>> = Vec::with_capacity(m + n + 2 * k);
152
153 for (idx, &(u, v)) in source_edges.iter().enumerate() {
155 target_edges.push((u, v));
156 target_edge_weights.push(source_edge_costs[idx]);
157 target_to_source_edge.push(Some(idx));
158 target_to_include_vertex.push(None);
159 }
160
161 for v in 0..n {
163 target_edges.push((v, root));
164 target_edge_weights.push(omega);
165 target_to_source_edge.push(None);
166 target_to_include_vertex.push(None);
167 }
168
169 for (gadget_pos, &v) in prized.iter().enumerate() {
172 let t_v = gadget_terminal(gadget_pos);
173 target_edges.push((v, t_v));
175 target_edge_weights.push(0);
176 target_to_source_edge.push(None);
177 target_to_include_vertex.push(Some(v));
178 target_edges.push((root, t_v));
180 target_edge_weights.push(beta * source_prizes[v]);
181 target_to_source_edge.push(None);
182 target_to_include_vertex.push(None);
183 }
184
185 let mut terminals: Vec<usize> = Vec::with_capacity(k + 1);
187 terminals.push(root);
188 for gadget_pos in 0..k {
189 terminals.push(gadget_terminal(gadget_pos));
190 }
191
192 let target_graph = SimpleGraph::new(target_num_vertices, target_edges);
193 let target =
194 SteinerTree::<SimpleGraph, i32>::new(target_graph, target_edge_weights, terminals);
195
196 ReductionPCSFToSteinerTree {
197 target,
198 num_source_vertices: n,
199 num_source_edges: m,
200 target_to_source_edge,
201 target_to_include_vertex,
202 }
203 }
204}
205
206#[cfg(feature = "example-db")]
207pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
208 use crate::example_db::specs::RuleExampleSpec;
209 use crate::export::SolutionPair;
210 use crate::solvers::BruteForce;
211
212 vec![RuleExampleSpec {
213 id: "prize_collecting_steiner_forest_to_steiner_tree",
214 build: || {
215 let source = PrizeCollectingSteinerForest::<SimpleGraph, i32>::new(
221 SimpleGraph::new(3, vec![(0, 1), (1, 2)]),
222 vec![5, 1, 5],
223 vec![10, 10],
224 1,
225 1,
226 );
227 let reduction = <PrizeCollectingSteinerForest<SimpleGraph, i32> as ReduceTo<
228 SteinerTree<SimpleGraph, i32>,
229 >>::reduce_to(&source);
230 let target = reduction.target_problem();
231 let target_config = BruteForce::new()
232 .find_witness(target)
233 .expect("canonical PCSF -> SteinerTree example must have an optimal target tree");
234 let source_config = reduction.extract_solution(&target_config);
235 crate::example_db::specs::assemble_rule_example(
236 &source,
237 target,
238 vec![SolutionPair {
239 source_config,
240 target_config,
241 }],
242 )
243 },
244 }]
245}
246
247#[cfg(test)]
248#[path = "../unit_tests/rules/prizecollectingsteinerforest_steinertree.rs"]
249mod tests;