problemreductions/rules/unitdiskmapping/
weighted.rs1use super::ksg::MappingResult;
4use super::triangular::gadgets::{tape_entry_center_transform, tape_entry_size};
5use super::{mapping_integer_overflow, mapping_invalid, mapping_non_finite};
6use crate::rules::ReductionError;
7use crate::types::i64_to_exact_f64;
8use std::collections::HashMap;
9
10pub fn trace_centers(result: &MappingResult) -> Result<Vec<(usize, usize)>, ReductionError> {
12 let mut centers = result
13 .lines
14 .iter()
15 .map(|line| {
16 let (row, column) = line.center_location(result.padding, result.spacing);
17 column
18 .checked_add(1)
19 .map(|column| (row, column))
20 .ok_or(mapping_integer_overflow(
21 "offsetting a triangular copy-line center",
22 ))
23 })
24 .collect::<Result<Vec<_>, _>>()?;
25
26 for entry in &result.tape {
27 let (height, width) = tape_entry_size(entry.pattern_idx).ok_or(mapping_invalid(
28 "mapping result contains an unknown triangular gadget",
29 ))?;
30 let row_end = entry
31 .row
32 .checked_add(height)
33 .ok_or(mapping_integer_overflow(
34 "computing triangular gadget bounds",
35 ))?;
36 let column_end = entry
37 .col
38 .checked_add(width)
39 .ok_or(mapping_integer_overflow(
40 "computing triangular gadget bounds",
41 ))?;
42
43 let Some((source, shift)) = tape_entry_center_transform(entry.pattern_idx) else {
44 continue;
45 };
46 for center in &mut centers {
47 if center.0 >= entry.row
48 && center.0 < row_end
49 && center.1 >= entry.col
50 && center.1 < column_end
51 && (center.0 - entry.row + 1, center.1 - entry.col + 1) == source
52 {
53 center.0 = center
54 .0
55 .checked_add_signed(shift.0)
56 .ok_or(mapping_integer_overflow("moving a triangular center row"))?;
57 center.1 = center
58 .1
59 .checked_add_signed(shift.1)
60 .ok_or(mapping_integer_overflow(
61 "moving a triangular center column",
62 ))?;
63 }
64 }
65 }
66
67 let mut indexed = result
68 .lines
69 .iter()
70 .zip(centers)
71 .map(|(line, center)| (line.vertex, center))
72 .collect::<Vec<_>>();
73 indexed.sort_by_key(|(vertex, _)| *vertex);
74 Ok(indexed.into_iter().map(|(_, center)| center).collect())
75}
76
77pub fn map_weights(
79 result: &MappingResult,
80 source_weights: &[f64],
81) -> Result<Vec<f64>, ReductionError> {
82 if source_weights
83 .iter()
84 .any(|&weight| !weight.is_finite() || !(0.0..=1.0).contains(&weight))
85 {
86 return Err(mapping_invalid(
87 "source weights must be finite and in [0, 1]",
88 ));
89 }
90 if source_weights.len() != result.lines.len() {
91 return Err(mapping_invalid(
92 "source weight count must match the original vertex count",
93 ));
94 }
95
96 let mut weights = result
97 .node_weights
98 .iter()
99 .map(|&weight| {
100 i64_to_exact_f64(weight).map_err(|_| {
101 mapping_invalid("a mapped node weight is not exactly representable as f64")
102 })
103 })
104 .collect::<Result<Vec<_>, _>>()?;
105 let positions = result
106 .positions
107 .iter()
108 .enumerate()
109 .map(|(index, &(row, column))| {
110 let row = usize::try_from(row)
111 .map_err(|_| mapping_invalid("mapping result contains a negative grid row"))?;
112 let column = usize::try_from(column)
113 .map_err(|_| mapping_invalid("mapping result contains a negative grid column"))?;
114 Ok(((row, column), index))
115 })
116 .collect::<Result<HashMap<_, _>, ReductionError>>()?;
117
118 for (center, source_weight) in trace_centers(result)?.into_iter().zip(source_weights) {
119 let index = positions.get(¢er).copied().ok_or(mapping_invalid(
120 "a traced center is missing from the mapped graph",
121 ))?;
122 let weight = weights[index] + source_weight;
123 if !weight.is_finite() {
124 return Err(mapping_non_finite(
125 "adding a source weight to a mapped center",
126 ));
127 }
128 weights[index] = weight;
129 }
130
131 Ok(weights)
132}
133
134#[cfg(test)]
135#[path = "../../unit_tests/rules/unitdiskmapping/weighted.rs"]
136mod tests;