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(
163 transform = upper_bound {
164 num_vars = "string_len + string_len ^ 3",
165 num_constraints = "string_len + 1",
166 },
167 unavailable = {
168 num_nonzeros = "the exact target parameter is not represented by this reduction's symbolic transform",
169 }
170)]
171impl ReduceTo<ILP<bool>> for MinimumInternalMacroDataCompression {
172 type Result = ReductionIMDCToILP;
173
174 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
175 let n = self.string_len();
176 let k = self.alphabet_size();
177 let h = self.pointer_cost();
178 let s = self.string();
179
180 if n == 0 {
182 let layout = VarLayout::new(0, s);
183 let target = ILP::new(0, vec![], vec![], ObjectiveSense::Minimize)
184 .map_err(Self::target_construction)?;
185 return Ok(ReductionIMDCToILP {
186 target,
187 layout,
188 source_string: vec![],
189 alphabet_size: k,
190 });
191 }
192
193 let layout = VarLayout::new(n, s);
194 let num_vars = layout.total_vars;
195 let mut constraints = Vec::new();
196
197 let segment_terms = |i: usize, l: usize| -> Vec<(usize, i64)> {
207 let mut terms = Vec::new();
208 if l == 1 {
209 terms.push((layout.lit_var(i), 1));
210 }
211 for (idx, &(pi, pl, _)) in layout.ptr_triples.iter().enumerate() {
213 if pi == i && pl == l {
214 terms.push((layout.ptr_offset + idx, 1));
215 }
216 }
217 terms
218 };
219
220 for node in 0..=n {
221 let mut all_terms: Vec<(usize, i64)> = Vec::new();
222
223 if node == 0 {
224 for l in 1..=n {
225 all_terms.extend(segment_terms(0, l));
226 }
227 constraints.push(LinearConstraint::eq(all_terms, 1));
228 } else if node == n {
229 for j in 0..n {
230 let l = n - j;
231 all_terms.extend(segment_terms(j, l));
232 }
233 constraints.push(LinearConstraint::eq(all_terms, 1));
234 } else {
235 let mut incoming = Vec::new();
236 for j in 0..node {
237 let l = node - j;
238 incoming.extend(segment_terms(j, l));
239 }
240 let mut outgoing = Vec::new();
241 for l in 1..=(n - node) {
242 outgoing.extend(segment_terms(node, l));
243 }
244 for (var, coef) in incoming {
245 all_terms.push((var, coef));
246 }
247 for (var, coef) in outgoing {
248 all_terms.push((var, -coef));
249 }
250 constraints.push(LinearConstraint::eq(all_terms, 0));
251 }
252 }
253
254 let mut objective: Vec<(usize, i64)> = Vec::new();
265 for i in 0..n {
266 objective.push((layout.lit_var(i), 1));
267 }
268 for (idx, _) in layout.ptr_triples.iter().enumerate() {
269 objective.push((layout.ptr_offset + idx, h));
270 }
271
272 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Minimize)
273 .map_err(Self::target_construction)?;
274
275 Ok(ReductionIMDCToILP {
276 target,
277 layout,
278 source_string: s.to_vec(),
279 alphabet_size: k,
280 })
281 }
282}
283
284#[cfg(feature = "example-db")]
285pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
286 use crate::export::SolutionPair;
287
288 vec![crate::example_db::specs::RuleExampleSpec {
292 id: "minimuminternalmacrodatacompression_to_ilp",
293 build: || {
294 let source = MinimumInternalMacroDataCompression::new(2, vec![0, 1], 2);
295 let reduction =
296 ReduceTo::<ILP<bool>>::reduce_to(&source).expect("reduction should succeed");
297 let layout = &reduction.layout;
298
299 let mut target_config = vec![0_i64; layout.total_vars];
300 target_config[layout.lit_var(0)] = 1;
301 target_config[layout.lit_var(1)] = 1;
302
303 let source_config = reduction.extract_solution(&target_config).unwrap();
304
305 crate::example_db::specs::rule_example_with_witness::<_, ILP<bool>>(
306 source,
307 SolutionPair {
308 source_config: serde_json::to_value(source_config)
309 .expect("solution serialization must succeed"),
310 target_config: serde_json::to_value(target_config)
311 .expect("solution serialization must succeed"),
312 },
313 )
314 },
315 }]
316}
317
318#[cfg(test)]
319#[path = "../unit_tests/rules/minimuminternalmacrodatacompression_ilp.rs"]
320mod tests;