problemreductions/rules/unitdiskmapping/triangular/
mapping.rs1use super::super::copyline::{create_copylines, CopyLine};
7use super::super::grid::MappingGrid;
8use super::super::ksg::mapping::MappingResult;
9use super::super::ksg::KsgTapeEntry as TapeEntry;
10use super::super::pathdecomposition::{
11 pathwidth, vertex_order_from_layout, PathDecompositionMethod,
12};
13use super::gadgets::{apply_crossing_gadgets, apply_simplifier_gadgets, tape_entry_mis_overhead};
14use crate::rules::unitdiskmapping::ksg::mapping::GridKind;
15use crate::rules::unitdiskmapping::{mapping_integer_overflow, mapping_invalid};
16use crate::rules::ReductionError;
17use std::collections::HashMap;
18
19fn position_index(
20 result: &MappingResult,
21) -> Result<HashMap<(usize, usize), usize>, ReductionError> {
22 result
23 .positions
24 .iter()
25 .enumerate()
26 .map(|(index, &(row, column))| {
27 let row = usize::try_from(row)
28 .map_err(|_| mapping_invalid("mapping result contains a negative grid row"))?;
29 let column = usize::try_from(column)
30 .map_err(|_| mapping_invalid("mapping result contains a negative grid column"))?;
31 Ok(((row, column), index))
32 })
33 .collect()
34}
35
36pub const SPACING: usize = 6;
38
39pub const PADDING: usize = 2;
41
42fn crossat(
44 copylines: &[CopyLine],
45 v: usize,
46 w: usize,
47 spacing: usize,
48 padding: usize,
49) -> (usize, usize) {
50 let line_v = ©lines[v];
51 let line_w = ©lines[w];
52
53 let (line_first, line_second) = if line_v.vslot < line_w.vslot {
55 (line_v, line_w)
56 } else {
57 (line_w, line_v)
58 };
59
60 let hslot = line_first.hslot;
61 let max_vslot = line_second.vslot;
62
63 let row = (hslot - 1) * spacing + 1 + padding; let col = (max_vslot - 1) * spacing + padding; (row, col)
68}
69
70pub fn map_weighted(
95 num_vertices: usize,
96 edges: &[(usize, usize)],
97) -> Result<MappingResult, ReductionError> {
98 map_weighted_with_method(num_vertices, edges, PathDecompositionMethod::Auto)
99}
100
101pub fn map_weighted_with_method(
111 num_vertices: usize,
112 edges: &[(usize, usize)],
113 method: PathDecompositionMethod,
114) -> Result<MappingResult, ReductionError> {
115 let layout = pathwidth(num_vertices, edges, method);
116 let vertex_order = vertex_order_from_layout(&layout);
117 map_weighted_with_order(num_vertices, edges, &vertex_order)
118}
119
120pub fn map_weighted_with_order(
136 num_vertices: usize,
137 edges: &[(usize, usize)],
138 vertex_order: &[usize],
139) -> Result<MappingResult, ReductionError> {
140 if num_vertices == 0 {
141 return Err(mapping_invalid("num_vertices must be positive"));
142 }
143
144 let spacing = SPACING;
145 let padding = PADDING;
146
147 let copylines = create_copylines(num_vertices, edges, vertex_order)?;
148
149 let max_hslot = copylines.iter().map(|l| l.hslot).max().unwrap_or(1);
154 let max_vstop = copylines.iter().map(|l| l.vstop).max().unwrap_or(1);
155
156 let padding_twice = padding.checked_mul(2).ok_or(mapping_integer_overflow(
157 "computing triangular grid padding",
158 ))?;
159 let extent = |slots: usize| {
160 slots
161 .checked_mul(spacing)
162 .and_then(|value| value.checked_add(2))
163 .and_then(|value| value.checked_add(padding_twice))
164 .ok_or(mapping_integer_overflow(
165 "computing triangular grid dimensions",
166 ))
167 };
168 let rows = extent(max_hslot.max(max_vstop))?;
169 let cols = extent(num_vertices - 1)?;
171
172 let mut grid = MappingGrid::with_padding(rows, cols, spacing, padding);
173
174 for line in ©lines {
177 for (row, col, weight) in line.copyline_locations_triangular(padding, spacing) {
178 let weight = i64::try_from(weight).map_err(|_| {
179 mapping_integer_overflow("converting a triangular grid weight to i64")
180 })?;
181 grid.add_node(row, col, weight);
182 }
183 }
184
185 for &(u, v) in edges {
187 let u_line = ©lines[u];
188 let v_line = ©lines[v];
189
190 let (smaller_line, larger_line) = if u_line.vslot < v_line.vslot {
191 (u_line, v_line)
192 } else {
193 (v_line, u_line)
194 };
195
196 let (row, col) = crossat(
197 ©lines,
198 smaller_line.vertex,
199 larger_line.vertex,
200 spacing,
201 padding,
202 );
203
204 if col > 0 {
206 grid.connect(row, col - 1);
207 }
208 if row > 0 && grid.is_occupied(row - 1, col) {
209 grid.connect(row - 1, col);
210 } else if row + 1 < grid.size().0 && grid.is_occupied(row + 1, col) {
211 grid.connect(row + 1, col);
212 }
213 }
214
215 let mut triangular_tape = apply_crossing_gadgets(&mut grid, ©lines, spacing, padding);
217
218 let simplifier_tape = apply_simplifier_gadgets(&mut grid, 10);
222 triangular_tape.extend(simplifier_tape);
223
224 let copyline_overhead = copylines.iter().try_fold(0_i64, |total, line| {
227 total
228 .checked_add(super::super::copyline::mis_overhead_copyline_triangular(
229 line, spacing,
230 )?)
231 .ok_or(mapping_integer_overflow(
232 "summing triangular copy-line MIS overhead",
233 ))
234 })?;
235
236 let gadget_overhead = triangular_tape.iter().try_fold(0_i64, |total, entry| {
238 total
239 .checked_add(tape_entry_mis_overhead(entry)?)
240 .ok_or(mapping_integer_overflow(
241 "summing triangular gadget MIS overhead",
242 ))
243 })?;
244 let mis_overhead =
245 copyline_overhead
246 .checked_add(gadget_overhead)
247 .ok_or(mapping_integer_overflow(
248 "computing total triangular MIS overhead",
249 ))?;
250
251 if grid.has_unresolved_cells() {
252 return Err(mapping_invalid(
253 "triangular mapping left doubled or connected cells unresolved",
254 ));
255 }
256
257 let tape: Vec<TapeEntry> = triangular_tape
259 .into_iter()
260 .map(|entry| TapeEntry {
261 pattern_idx: entry.gadget_idx,
262 row: entry.row,
263 col: entry.col,
264 })
265 .collect();
266
267 let doubled_cells = grid.doubled_cells();
269
270 let positions_and_weights = grid
272 .occupied_coords()
273 .into_iter()
274 .filter_map(|(row, col)| {
275 grid.get(row, col)
276 .filter(|cell| cell.weight() > 0)
277 .map(|cell| {
278 Ok((
279 (
280 i64::try_from(row).map_err(|_| {
281 mapping_integer_overflow("converting a grid row to i64")
282 })?,
283 i64::try_from(col).map_err(|_| {
284 mapping_integer_overflow("converting a grid column to i64")
285 })?,
286 ),
287 cell.weight(),
288 ))
289 })
290 })
291 .collect::<Result<Vec<_>, ReductionError>>()?;
292 let (positions, node_weights): (Vec<_>, Vec<_>) = positions_and_weights.into_iter().unzip();
293
294 Ok(MappingResult {
295 positions,
296 node_weights,
297 grid_dimensions: grid.size(),
298 kind: GridKind::Triangular,
299 lines: copylines,
300 padding,
301 spacing,
302 mis_overhead,
303 tape,
304 doubled_cells,
305 })
306}
307
308pub fn map_config_back(
310 result: &MappingResult,
311 grid_config: &[usize],
312) -> crate::rules::ExtractionResult<Vec<usize>> {
313 map_config_back_internal(result, grid_config)
314 .map_err(|error| crate::rules::ExtractionError::invalid(error.to_string()))
315}
316
317fn map_config_back_internal(
318 result: &MappingResult,
319 grid_config: &[usize],
320) -> Result<Vec<usize>, ReductionError> {
321 if grid_config.len() != result.positions.len() {
322 return Err(mapping_invalid(
323 "grid configuration length must match the mapped vertex count",
324 ));
325 }
326 let positions = position_index(result)?;
327
328 super::super::weighted::trace_centers(result)?
329 .into_iter()
330 .map(|center| {
331 positions
332 .get(¢er)
333 .map(|&index| grid_config[index])
334 .ok_or(mapping_invalid(
335 "a traced center is missing from the mapped graph",
336 ))
337 })
338 .collect()
339}
340
341pub fn map_unit_weights(result: &MappingResult) -> Result<Vec<i64>, ReductionError> {
347 let count = i64::try_from(result.lines.len())
348 .map_err(|_| mapping_integer_overflow("converting the source vertex count to i64"))?;
349 let scale = count.checked_add(1).ok_or(mapping_integer_overflow(
350 "computing the unit-weight encoding scale",
351 ))?;
352 let mut weights = result
353 .node_weights
354 .iter()
355 .map(|weight| {
356 weight.checked_mul(scale).ok_or(mapping_integer_overflow(
357 "scaling a triangular mapped weight",
358 ))
359 })
360 .collect::<Result<Vec<_>, _>>()?;
361 let positions = position_index(result)?;
362
363 for center in super::super::weighted::trace_centers(result)? {
364 let index = positions.get(¢er).copied().ok_or(mapping_invalid(
365 "a traced center is missing from the mapped graph",
366 ))?;
367 weights[index] = weights[index]
368 .checked_add(1)
369 .ok_or(mapping_integer_overflow(
370 "adding a unit source weight to a triangular center",
371 ))?;
372 }
373 Ok(weights)
374}
375
376#[cfg(test)]
377#[path = "../../../unit_tests/rules/unitdiskmapping/triangular/mapping.rs"]
378mod tests;