1use super::super::copyline::{create_copylines, mis_overhead_copyline, CopyLine};
7use super::super::grid::MappingGrid;
8use super::super::pathdecomposition::{
9 pathwidth, vertex_order_from_layout, PathDecompositionMethod,
10};
11use super::gadgets::{
12 apply_crossing_gadgets, apply_simplifier_gadgets, tape_entry_mis_overhead, KsgPattern,
13 KsgTapeEntry,
14};
15use super::gadgets_weighted::{
16 apply_weighted_crossing_gadgets, apply_weighted_simplifier_gadgets,
17 weighted_tape_entry_mis_overhead, WeightedKsgPattern, WeightedKsgTapeEntry,
18};
19use super::{PADDING, SPACING};
20use crate::topology::{Graph, KingsSubgraph, TriangularSubgraph};
21use serde::{Deserialize, Serialize};
22use std::collections::{HashMap, HashSet};
23use std::fmt;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
27pub enum GridKind {
28 Kings,
30 Triangular,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct MappingResult<T = KsgTapeEntry> {
37 pub positions: Vec<(i32, i32)>,
39 pub node_weights: Vec<i32>,
41 pub grid_dimensions: (usize, usize),
43 pub kind: GridKind,
45 pub lines: Vec<CopyLine>,
47 pub padding: usize,
49 pub spacing: usize,
51 pub mis_overhead: i32,
53 pub tape: Vec<T>,
55 #[serde(default)]
57 pub doubled_cells: HashSet<(usize, usize)>,
58}
59
60impl<T> MappingResult<T> {
61 pub fn num_original_vertices(&self) -> usize {
63 self.lines.len()
64 }
65
66 pub fn edges(&self) -> Vec<(usize, usize)> {
68 match self.kind {
69 GridKind::Kings => self.to_kings_subgraph().edges(),
70 GridKind::Triangular => self.to_triangular_subgraph().edges(),
71 }
72 }
73
74 pub fn num_edges(&self) -> usize {
76 match self.kind {
77 GridKind::Kings => self.to_kings_subgraph().num_edges(),
78 GridKind::Triangular => self.to_triangular_subgraph().num_edges(),
79 }
80 }
81
82 pub fn print_config(&self, config: &[Vec<usize>]) {
89 print!("{}", self.format_config(config));
90 }
91
92 pub fn format_config(&self, config: &[Vec<usize>]) -> String {
94 let (rows, cols) = self.grid_dimensions;
95
96 let mut pos_to_node: HashMap<(i32, i32), usize> = HashMap::new();
98 for (idx, &(r, c)) in self.positions.iter().enumerate() {
99 pos_to_node.insert((r, c), idx);
100 }
101
102 let mut lines = Vec::new();
103
104 for r in 0..rows {
105 let mut line = String::new();
106 for c in 0..cols {
107 let is_selected = config
108 .get(r)
109 .and_then(|row| row.get(c))
110 .copied()
111 .unwrap_or(0)
112 > 0;
113 let has_node = pos_to_node.contains_key(&(r as i32, c as i32));
114
115 let s = if has_node {
116 if is_selected {
117 "*"
118 } else {
119 "o"
120 }
121 } else {
122 "."
123 };
124 line.push_str(s);
125 line.push(' ');
126 }
127 line.pop();
129 lines.push(line);
130 }
131
132 lines.join("\n")
133 }
134
135 pub fn print_config_flat(&self, config: &[usize]) {
137 print!("{}", self.format_config_flat(config));
138 }
139
140 pub fn format_config_flat(&self, config: &[usize]) -> String {
142 self.format_grid_with_config(Some(config))
143 }
144
145 pub fn to_kings_subgraph(&self) -> KingsSubgraph {
148 KingsSubgraph::new(self.positions.clone())
149 }
150
151 pub fn to_triangular_subgraph(&self) -> TriangularSubgraph {
154 TriangularSubgraph::new(self.positions.clone())
155 }
156
157 fn format_grid_with_config(&self, config: Option<&[usize]>) -> String {
163 if self.positions.is_empty() {
164 return String::from("(empty grid graph)");
165 }
166
167 let (rows, cols) = self.grid_dimensions;
168
169 let mut pos_to_idx: HashMap<(i32, i32), usize> = HashMap::new();
170 for (idx, &(r, c)) in self.positions.iter().enumerate() {
171 pos_to_idx.insert((r, c), idx);
172 }
173
174 let mut lines = Vec::new();
175
176 for r in 0..rows as i32 {
177 let mut line = String::new();
178 for c in 0..cols as i32 {
179 let s = if let Some(&idx) = pos_to_idx.get(&(r, c)) {
180 if let Some(cfg) = config {
181 if cfg.get(idx).copied().unwrap_or(0) > 0 {
182 "●".to_string()
183 } else {
184 "○".to_string()
185 }
186 } else {
187 let w = self.node_weights[idx];
188 let ws = format!("{}", w);
189 if ws.len() == 1 {
190 ws
191 } else {
192 "●".to_string()
193 }
194 }
195 } else {
196 "⋅".to_string()
197 };
198 line.push_str(&s);
199 line.push(' ');
200 }
201 line.pop();
202 lines.push(line);
203 }
204
205 lines.join("\n")
206 }
207}
208
209impl MappingResult<KsgTapeEntry> {
210 pub fn map_config_back(&self, grid_config: &[usize]) -> Vec<usize> {
223 let (rows, cols) = self.grid_dimensions;
225 let mut config_2d = vec![vec![0usize; cols]; rows];
226
227 for (idx, &(row, col)) in self.positions.iter().enumerate() {
228 let row = row as usize;
229 let col = col as usize;
230 if row < rows && col < cols {
231 config_2d[row][col] = grid_config.get(idx).copied().unwrap_or(0);
232 }
233 }
234
235 unapply_gadgets(&self.tape, &mut config_2d);
237
238 map_config_copyback(
240 &self.lines,
241 self.padding,
242 self.spacing,
243 &config_2d,
244 &self.doubled_cells,
245 )
246 }
247
248 pub fn map_config_back_via_centers(&self, grid_config: &[usize]) -> Vec<usize> {
250 let mut pos_to_idx: HashMap<(usize, usize), usize> = HashMap::new();
252 for (idx, &(row, col)) in self.positions.iter().enumerate() {
253 if let (Ok(row), Ok(col)) = (usize::try_from(row), usize::try_from(col)) {
254 pos_to_idx.insert((row, col), idx);
255 }
256 }
257
258 let centers = trace_centers(self);
260 let num_vertices = centers.len();
261 let mut result = vec![0usize; num_vertices];
262
263 for (vertex, &(row, col)) in centers.iter().enumerate() {
265 if let Some(&node_idx) = pos_to_idx.get(&(row, col)) {
266 result[vertex] = grid_config.get(node_idx).copied().unwrap_or(0);
267 }
268 }
269
270 result
271 }
272}
273
274impl MappingResult<WeightedKsgTapeEntry> {
275 pub fn map_config_back(&self, grid_config: &[usize]) -> Vec<usize> {
277 let (rows, cols) = self.grid_dimensions;
279 let mut config_2d = vec![vec![0usize; cols]; rows];
280
281 for (idx, &(row, col)) in self.positions.iter().enumerate() {
282 let row = row as usize;
283 let col = col as usize;
284 if row < rows && col < cols {
285 config_2d[row][col] = grid_config.get(idx).copied().unwrap_or(0);
286 }
287 }
288
289 unapply_weighted_gadgets(&self.tape, &mut config_2d);
291
292 map_config_copyback(
294 &self.lines,
295 self.padding,
296 self.spacing,
297 &config_2d,
298 &self.doubled_cells,
299 )
300 }
301}
302
303impl<T> fmt::Display for MappingResult<T> {
304 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305 write!(f, "{}", self.format_grid_with_config(None))
306 }
307}
308
309pub fn map_config_copyback(
316 lines: &[CopyLine],
317 padding: usize,
318 spacing: usize,
319 config: &[Vec<usize>],
320 doubled_cells: &HashSet<(usize, usize)>,
321) -> Vec<usize> {
322 let mut result = vec![0usize; lines.len()];
323
324 for line in lines {
325 let locs = line.copyline_locations(padding, spacing);
326 let n = locs.len();
327 let mut count = 0i32;
328
329 for (iloc, &(row, col, weight)) in locs.iter().enumerate() {
330 let ci = config
331 .get(row)
332 .and_then(|r| r.get(col))
333 .copied()
334 .unwrap_or(0);
335
336 if doubled_cells.contains(&(row, col)) {
338 if ci == 2 {
340 count += 1;
341 } else if ci == 1 {
342 let prev_zero = if iloc > 0 {
344 let (pr, pc, _) = locs[iloc - 1];
345 config.get(pr).and_then(|r| r.get(pc)).copied().unwrap_or(0) == 0
346 } else {
347 true
348 };
349 let next_zero = if iloc + 1 < n {
350 let (nr, nc, _) = locs[iloc + 1];
351 config.get(nr).and_then(|r| r.get(nc)).copied().unwrap_or(0) == 0
352 } else {
353 true
354 };
355 if prev_zero && next_zero {
356 count += 1;
357 }
358 }
359 } else if weight >= 1 {
361 count += ci as i32;
363 }
364 }
366
367 let overhead = (n / 2) as i32;
369 result[line.vertex] = (count - overhead).max(0) as usize;
371 }
372
373 result
374}
375
376pub fn unapply_gadgets(tape: &[KsgTapeEntry], config: &mut [Vec<usize>]) {
378 for entry in tape.iter().rev() {
380 if let Some(pattern) = KsgPattern::from_tape_idx(entry.pattern_idx) {
381 pattern.map_config_back(entry.row, entry.col, config);
382 }
383 }
384}
385
386pub fn unapply_weighted_gadgets(tape: &[WeightedKsgTapeEntry], config: &mut [Vec<usize>]) {
388 for entry in tape.iter().rev() {
390 if let Some(pattern) = WeightedKsgPattern::from_tape_idx(entry.pattern_idx) {
391 pattern.map_config_back(entry.row, entry.col, config);
392 }
393 }
394}
395
396pub fn trace_centers(result: &MappingResult<KsgTapeEntry>) -> Vec<(usize, usize)> {
400 let mut centers: Vec<(usize, usize)> = result
402 .lines
403 .iter()
404 .map(|line| {
405 let (row, col) = line.center_location(result.padding, result.spacing);
406 (row, col + 1) })
408 .collect();
409
410 for entry in &result.tape {
412 let pattern_idx = entry.pattern_idx;
413 let gi = entry.row;
414 let gj = entry.col;
415
416 if pattern_idx >= 100 {
420 let simplifier_idx = pattern_idx - 100;
422 let (m, n, source_center, mapped_center) = match simplifier_idx {
423 0 => (4, 3, (2, 2), (4, 2)), 1 => (3, 4, (2, 2), (2, 4)), 2 => (4, 3, (3, 2), (1, 2)), 3 => (3, 4, (2, 3), (2, 1)), 4 => (4, 3, (2, 2), (4, 2)), 5 => (4, 3, (2, 2), (4, 2)), _ => continue,
430 };
431
432 for center in centers.iter_mut() {
434 let (ci, cj) = *center;
435
436 if ci >= gi && ci < gi + m && cj >= gj && cj < gj + n {
438 let local_i = ci - gi + 1;
440 let local_j = cj - gj + 1;
441
442 if local_i == source_center.0 && local_j == source_center.1 {
444 *center = (gi + mapped_center.0 - 1, gj + mapped_center.1 - 1);
446 }
447 }
448 }
449 }
450 }
452
453 let mut indexed: Vec<_> = result
455 .lines
456 .iter()
457 .enumerate()
458 .map(|(idx, line)| (line.vertex, centers[idx]))
459 .collect();
460 indexed.sort_by_key(|(v, _)| *v);
461 indexed.into_iter().map(|(_, c)| c).collect()
462}
463
464fn embed_graph_internal(
466 num_vertices: usize,
467 edges: &[(usize, usize)],
468 vertex_order: &[usize],
469) -> Option<(MappingGrid, Vec<CopyLine>)> {
470 if num_vertices == 0 {
471 return None;
472 }
473
474 let copylines = create_copylines(num_vertices, edges, vertex_order);
475
476 let max_hslot = copylines.iter().map(|l| l.hslot).max().unwrap_or(1);
478
479 let rows = max_hslot * SPACING + 2 + 2 * PADDING;
480 let cols = (num_vertices - 1) * SPACING + 2 + 2 * PADDING;
481
482 let mut grid = MappingGrid::with_padding(rows, cols, SPACING, PADDING);
483
484 for line in ©lines {
486 for (row, col, weight) in line.copyline_locations(PADDING, SPACING) {
487 grid.add_node(row, col, weight as i32);
488 }
489 }
490
491 for &(u, v) in edges {
493 let u_line = ©lines[u];
494 let v_line = ©lines[v];
495
496 let (smaller_line, larger_line) = if u_line.vslot < v_line.vslot {
497 (u_line, v_line)
498 } else {
499 (v_line, u_line)
500 };
501 let (row, col) = grid.cross_at(smaller_line.vslot, larger_line.vslot, smaller_line.hslot);
502
503 if col > 0 {
505 grid.connect(row, col - 1);
506 }
507 if row > 0 && grid.is_occupied(row - 1, col) {
508 grid.connect(row - 1, col);
509 } else if row + 1 < grid.size().0 && grid.is_occupied(row + 1, col) {
510 grid.connect(row + 1, col);
511 }
512 }
513
514 Some((grid, copylines))
515}
516
517pub fn embed_graph(
523 num_vertices: usize,
524 edges: &[(usize, usize)],
525 vertex_order: &[usize],
526) -> Option<MappingGrid> {
527 embed_graph_internal(num_vertices, edges, vertex_order).map(|(grid, _)| grid)
528}
529
530pub fn map_unweighted(
538 num_vertices: usize,
539 edges: &[(usize, usize)],
540) -> MappingResult<KsgTapeEntry> {
541 map_unweighted_with_method(num_vertices, edges, PathDecompositionMethod::Auto)
542}
543
544pub fn map_unweighted_with_method(
551 num_vertices: usize,
552 edges: &[(usize, usize)],
553 method: PathDecompositionMethod,
554) -> MappingResult<KsgTapeEntry> {
555 let layout = pathwidth(num_vertices, edges, method);
556 let vertex_order = vertex_order_from_layout(&layout);
557 map_unweighted_with_order(num_vertices, edges, &vertex_order)
558}
559
560pub fn map_unweighted_with_order(
566 num_vertices: usize,
567 edges: &[(usize, usize)],
568 vertex_order: &[usize],
569) -> MappingResult<KsgTapeEntry> {
570 let (mut grid, copylines) = embed_graph_internal(num_vertices, edges, vertex_order)
571 .expect("Failed to embed graph: num_vertices must be > 0");
572
573 let doubled_cells = grid.doubled_cells();
575
576 let crossing_tape = apply_crossing_gadgets(&mut grid, ©lines);
578
579 let simplifier_tape = apply_simplifier_gadgets(&mut grid, 2);
581
582 let mut tape = crossing_tape;
584 tape.extend(simplifier_tape);
585
586 let copyline_overhead: i32 = copylines
588 .iter()
589 .map(|line| mis_overhead_copyline(line, SPACING, PADDING) as i32)
590 .sum();
591
592 let gadget_overhead: i32 = tape.iter().map(tape_entry_mis_overhead).sum();
594 let mis_overhead = copyline_overhead + gadget_overhead;
595
596 debug_assert!(
599 !grid.has_unresolved_cells(),
600 "Mapping is not done: doubled or connected cells remain after gadget application"
601 );
602
603 let positions: Vec<(i32, i32)> = grid
607 .occupied_coords()
608 .into_iter()
609 .filter_map(|(row, col)| {
610 grid.get(row, col)
611 .filter(|cell| cell.weight() > 0)
612 .map(|_| (row as i32, col as i32))
613 })
614 .collect();
615 let node_weights = vec![1i32; positions.len()];
616
617 MappingResult {
618 positions,
619 node_weights,
620 grid_dimensions: grid.size(),
621 kind: GridKind::Kings,
622 lines: copylines,
623 padding: PADDING,
624 spacing: SPACING,
625 mis_overhead,
626 tape,
627 doubled_cells,
628 }
629}
630
631pub fn map_weighted(
640 num_vertices: usize,
641 edges: &[(usize, usize)],
642) -> MappingResult<WeightedKsgTapeEntry> {
643 map_weighted_with_method(num_vertices, edges, PathDecompositionMethod::Auto)
644}
645
646pub fn map_weighted_with_method(
653 num_vertices: usize,
654 edges: &[(usize, usize)],
655 method: PathDecompositionMethod,
656) -> MappingResult<WeightedKsgTapeEntry> {
657 let layout = pathwidth(num_vertices, edges, method);
658 let vertex_order = vertex_order_from_layout(&layout);
659 map_weighted_with_order(num_vertices, edges, &vertex_order)
660}
661
662pub fn map_weighted_with_order(
668 num_vertices: usize,
669 edges: &[(usize, usize)],
670 vertex_order: &[usize],
671) -> MappingResult<WeightedKsgTapeEntry> {
672 let (mut grid, copylines) = embed_graph_internal(num_vertices, edges, vertex_order)
673 .expect("Failed to embed graph: num_vertices must be > 0");
674
675 let doubled_cells = grid.doubled_cells();
677
678 let crossing_tape = apply_weighted_crossing_gadgets(&mut grid, ©lines);
680
681 let simplifier_tape = apply_weighted_simplifier_gadgets(&mut grid, 2);
683
684 let mut tape = crossing_tape;
686 tape.extend(simplifier_tape);
687
688 let copyline_overhead: i32 = copylines
690 .iter()
691 .map(|line| mis_overhead_copyline(line, SPACING, PADDING) as i32 * 2)
692 .sum();
693
694 let gadget_overhead: i32 = tape.iter().map(weighted_tape_entry_mis_overhead).sum();
696 let mis_overhead = copyline_overhead + gadget_overhead;
697
698 debug_assert!(
700 !grid.has_unresolved_cells(),
701 "Mapping is not done: doubled or connected cells remain after gadget application"
702 );
703
704 let (positions, node_weights): (Vec<(i32, i32)>, Vec<i32>) = grid
706 .occupied_coords()
707 .into_iter()
708 .filter_map(|(row, col)| {
709 grid.get(row, col)
710 .map(|cell| ((row as i32, col as i32), cell.weight()))
711 })
712 .filter(|&(_, w)| w > 0)
713 .unzip();
714
715 MappingResult {
716 positions,
717 node_weights,
718 grid_dimensions: grid.size(),
719 kind: GridKind::Kings,
720 lines: copylines,
721 padding: PADDING,
722 spacing: SPACING,
723 mis_overhead,
724 tape,
725 doubled_cells,
726 }
727}
728
729#[cfg(test)]
730#[path = "../../../unit_tests/rules/unitdiskmapping/ksg/mapping.rs"]
731mod tests;