1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
19use crate::models::misc::MinimumInternalMacroDataCompression;
20use crate::reduction;
21use crate::rules::traits::{ReduceTo, ReductionResult};
22
23#[derive(Debug, Clone)]
25struct VarLayout {
26 n: usize,
27 lit_offset: usize,
29 ptr_offset: usize,
32 ptr_triples: Vec<(usize, usize, usize)>,
34 total_vars: usize,
36}
37
38impl VarLayout {
39 fn new(n: usize, source_string: &[usize]) -> Self {
40 let lit_offset = 0;
41 let ptr_offset = lit_offset + n;
42
43 let mut ptr_triples = Vec::new();
50 for i in 0..n {
51 for l in 1..=(n - i) {
52 for r in 0..i {
53 if r + l <= i
57 && r + l <= n
58 && source_string[r..r + l] == source_string[i..i + l]
59 {
60 ptr_triples.push((i, l, r));
61 }
62 }
63 }
64 }
65
66 let total_vars = ptr_offset + ptr_triples.len();
67 Self {
68 n,
69 lit_offset,
70 ptr_offset,
71 ptr_triples,
72 total_vars,
73 }
74 }
75
76 fn lit_var(&self, i: usize) -> usize {
77 self.lit_offset + i
78 }
79}
80
81#[derive(Debug, Clone)]
83pub struct ReductionIMDCToILP {
84 target: ILP<bool>,
85 layout: VarLayout,
86 source_string: Vec<usize>,
87 alphabet_size: usize,
88}
89
90impl ReductionResult for ReductionIMDCToILP {
91 type Source = MinimumInternalMacroDataCompression;
92 type Target = ILP<bool>;
93
94 fn target_problem(&self) -> &ILP<bool> {
95 &self.target
96 }
97
98 fn extract_solution(
99 &self,
100 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
101 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
102 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
103
104 Ok({
105 let n = self.layout.n;
106 let k = self.alphabet_size;
107 let eos = k; let mut source_to_c_pos = vec![0usize; n];
112 let mut segments: Vec<(usize, usize, Option<usize>)> = Vec::new(); let mut c_pos = 0;
114 let mut pos = 0;
115
116 while pos < n {
117 if target_solution[self.layout.lit_var(pos)] == 1 {
118 source_to_c_pos[pos] = c_pos;
119 segments.push((pos, 1, None));
120 c_pos += 1;
121 pos += 1;
122 continue;
123 }
124 let mut found = false;
125 for (idx, &(i, l, r)) in self.layout.ptr_triples.iter().enumerate() {
126 if i == pos && target_solution[self.layout.ptr_offset + idx] == 1 {
127 for offset in 0..l {
128 source_to_c_pos[pos + offset] = c_pos;
129 }
130 segments.push((pos, l, Some(r)));
131 c_pos += 1;
132 pos += l;
133 found = true;
134 break;
135 }
136 }
137 if !found {
138 pos += 1;
139 }
140 }
141
142 let mut config = vec![eos; n];
144 for (idx, &(src_start, _len, ref_pos)) in segments.iter().enumerate() {
145 match ref_pos {
146 None => {
147 config[idx] = self.source_string[src_start];
148 }
149 Some(r) => {
150 config[idx] = k + 1 + source_to_c_pos[r];
153 }
154 }
155 }
156
157 config
158 })
159 }
160}
161
162#[reduction(transform = upper_bound {
169 max_constraint_magnitude_bits = "1",
170 num_vars = "string_len + string_len * (string_len - 1) * (string_len + 3) / 12",
171 num_constraints = "string_len + 1",
172 num_nonzeros = "2 * string_len + string_len * (string_len - 1) * (string_len + 3) / 6",
173})]
174impl ReduceTo<ILP<bool>> for MinimumInternalMacroDataCompression {
175 type Result = ReductionIMDCToILP;
176
177 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
178 let n = self.string_len();
179 let k = self.alphabet_size();
180 let h = self.pointer_cost();
181 let s = self.string();
182
183 if n == 0 {
185 let layout = VarLayout::new(0, s);
186 let target = ILP::new(0, vec![], vec![], ObjectiveSense::Minimize)
187 .map_err(Self::target_construction)?;
188 return Ok(ReductionIMDCToILP {
189 target,
190 layout,
191 source_string: vec![],
192 alphabet_size: k,
193 });
194 }
195
196 let layout = VarLayout::new(n, s);
197 let num_vars = layout.total_vars;
198 let mut constraints = Vec::new();
199
200 let segment_terms = |i: usize, l: usize| -> Vec<(usize, i64)> {
210 let mut terms = Vec::new();
211 if l == 1 {
212 terms.push((layout.lit_var(i), 1));
213 }
214 for (idx, &(pi, pl, _)) in layout.ptr_triples.iter().enumerate() {
216 if pi == i && pl == l {
217 terms.push((layout.ptr_offset + idx, 1));
218 }
219 }
220 terms
221 };
222
223 for node in 0..=n {
224 let mut all_terms: Vec<(usize, i64)> = Vec::new();
225
226 if node == 0 {
227 for l in 1..=n {
228 all_terms.extend(segment_terms(0, l));
229 }
230 constraints.push(LinearConstraint::eq(all_terms, 1));
231 } else if node == n {
232 for j in 0..n {
233 let l = n - j;
234 all_terms.extend(segment_terms(j, l));
235 }
236 constraints.push(LinearConstraint::eq(all_terms, 1));
237 } else {
238 let mut incoming = Vec::new();
239 for j in 0..node {
240 let l = node - j;
241 incoming.extend(segment_terms(j, l));
242 }
243 let mut outgoing = Vec::new();
244 for l in 1..=(n - node) {
245 outgoing.extend(segment_terms(node, l));
246 }
247 for (var, coef) in incoming {
248 all_terms.push((var, coef));
249 }
250 for (var, coef) in outgoing {
251 all_terms.push((var, -coef));
252 }
253 constraints.push(LinearConstraint::eq(all_terms, 0));
254 }
255 }
256
257 let mut objective: Vec<(usize, i64)> = Vec::new();
268 for i in 0..n {
269 objective.push((layout.lit_var(i), 1));
270 }
271 for (idx, _) in layout.ptr_triples.iter().enumerate() {
272 objective.push((layout.ptr_offset + idx, h));
273 }
274
275 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Minimize)
276 .map_err(Self::target_construction)?;
277
278 Ok(ReductionIMDCToILP {
279 target,
280 layout,
281 source_string: s.to_vec(),
282 alphabet_size: k,
283 })
284 }
285}
286
287#[cfg(feature = "example-db")]
288pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
289 use crate::export::SolutionPair;
290
291 vec![crate::example_db::specs::RuleExampleSpec {
295 id: "minimuminternalmacrodatacompression_to_ilp",
296 build: || {
297 let source = MinimumInternalMacroDataCompression::new(2, vec![0, 1], 2);
298 let reduction =
299 ReduceTo::<ILP<bool>>::reduce_to(&source).expect("reduction should succeed");
300 let layout = &reduction.layout;
301
302 let mut target_config = vec![0_i64; layout.total_vars];
303 target_config[layout.lit_var(0)] = 1;
304 target_config[layout.lit_var(1)] = 1;
305
306 let source_config = reduction.extract_solution(&target_config).unwrap();
307
308 crate::example_db::specs::rule_example_with_witness::<_, ILP<bool>>(
309 source,
310 SolutionPair {
311 source_config: serde_json::to_value(source_config)
312 .expect("solution serialization must succeed"),
313 target_config: serde_json::to_value(target_config)
314 .expect("solution serialization must succeed"),
315 },
316 )
317 },
318 }]
319}
320
321#[cfg(test)]
322#[path = "../unit_tests/rules/minimuminternalmacrodatacompression_ilp.rs"]
323mod tests;