|
| 1 | +// # LongestCommonSubsequence to ILP Reduction |
| 2 | +// |
| 3 | +// ## Mathematical Formulation |
| 4 | +// Uses the match-pair formulation (Blum et al., 2021). |
| 5 | +// For each position pair (j1, j2) where s1[j1] == s2[j2], a binary variable m_{j1,j2}. |
| 6 | +// Constraints: |
| 7 | +// (1) Each s1 position matched at most once |
| 8 | +// (2) Each s2 position matched at most once |
| 9 | +// (3) Order preservation: no crossings among matched pairs |
| 10 | +// Objective: maximize total matched pairs. |
| 11 | +// |
| 12 | +// ## This Example |
| 13 | +// - Instance: s1 = "ABAC", s2 = "BACA" |
| 14 | +// - 6 match pairs, LCS = "BAC" (length 3) |
| 15 | +// |
| 16 | +// ## Output |
| 17 | +// Exports `docs/paper/examples/longestcommonsubsequence_to_ilp.json`. |
| 18 | + |
| 19 | +use problemreductions::export::*; |
| 20 | +use problemreductions::models::algebraic::ILP; |
| 21 | +use problemreductions::prelude::*; |
| 22 | +use problemreductions::solvers::ILPSolver; |
| 23 | + |
| 24 | +pub fn run() { |
| 25 | + // 1. Create LCS instance: s1 = "ABAC", s2 = "BACA" |
| 26 | + let problem = LongestCommonSubsequence::new(vec![ |
| 27 | + vec![b'A', b'B', b'A', b'C'], |
| 28 | + vec![b'B', b'A', b'C', b'A'], |
| 29 | + ]); |
| 30 | + |
| 31 | + // 2. Reduce to ILP |
| 32 | + let reduction = ReduceTo::<ILP<bool>>::reduce_to(&problem); |
| 33 | + let ilp = reduction.target_problem(); |
| 34 | + |
| 35 | + // 3. Print transformation |
| 36 | + println!("\n=== Problem Transformation ==="); |
| 37 | + println!( |
| 38 | + "Source: LCS with {} strings, total length {}", |
| 39 | + problem.num_strings(), |
| 40 | + problem.total_length() |
| 41 | + ); |
| 42 | + println!( |
| 43 | + "Target: ILP with {} variables, {} constraints", |
| 44 | + ilp.num_vars, |
| 45 | + ilp.constraints.len() |
| 46 | + ); |
| 47 | + |
| 48 | + // 4. Solve ILP |
| 49 | + let solver = ILPSolver::new(); |
| 50 | + let ilp_solution = solver |
| 51 | + .solve(ilp) |
| 52 | + .expect("ILP should be feasible for ABAC/BACA"); |
| 53 | + println!("\n=== Solution ==="); |
| 54 | + println!("ILP solution: {:?}", &ilp_solution); |
| 55 | + |
| 56 | + // 5. Extract LCS solution |
| 57 | + let extracted = reduction.extract_solution(&ilp_solution); |
| 58 | + println!("Source LCS config: {:?}", extracted); |
| 59 | + |
| 60 | + // 6. Verify |
| 61 | + let metric = problem.evaluate(&extracted); |
| 62 | + assert!(metric.is_valid()); |
| 63 | + let lcs_length = metric.unwrap(); |
| 64 | + println!("LCS length: {}", lcs_length); |
| 65 | + assert_eq!(lcs_length, 3); |
| 66 | + println!("\nReduction verified successfully"); |
| 67 | + |
| 68 | + // 7. Collect solutions and export JSON |
| 69 | + let solutions = vec![SolutionPair { |
| 70 | + source_config: extracted, |
| 71 | + target_config: ilp_solution, |
| 72 | + }]; |
| 73 | + |
| 74 | + let source_variant = variant_to_map(LongestCommonSubsequence::variant()); |
| 75 | + let target_variant = variant_to_map(ILP::<bool>::variant()); |
| 76 | + let overhead = |
| 77 | + lookup_overhead("LongestCommonSubsequence", &source_variant, "ILP", &target_variant) |
| 78 | + .expect("LCS -> ILP overhead not found"); |
| 79 | + |
| 80 | + let data = ReductionData { |
| 81 | + source: ProblemSide { |
| 82 | + problem: LongestCommonSubsequence::NAME.to_string(), |
| 83 | + variant: source_variant, |
| 84 | + instance: serde_json::json!({ |
| 85 | + "strings": [ |
| 86 | + [65, 66, 65, 67], |
| 87 | + [66, 65, 67, 65], |
| 88 | + ], |
| 89 | + }), |
| 90 | + }, |
| 91 | + target: ProblemSide { |
| 92 | + problem: ILP::<bool>::NAME.to_string(), |
| 93 | + variant: target_variant, |
| 94 | + instance: serde_json::json!({ |
| 95 | + "num_vars": ilp.num_vars, |
| 96 | + "num_constraints": ilp.constraints.len(), |
| 97 | + }), |
| 98 | + }, |
| 99 | + overhead: overhead_to_json(&overhead), |
| 100 | + }; |
| 101 | + |
| 102 | + let results = ResultData { solutions }; |
| 103 | + let name = "longestcommonsubsequence_to_ilp"; |
| 104 | + write_example(name, &data, &results); |
| 105 | +} |
| 106 | + |
| 107 | +fn main() { |
| 108 | + run() |
| 109 | +} |
0 commit comments