Fix #110: Add LongestCommonSubsequence model and LCS to ILP reduction#598
Fix #110: Add LongestCommonSubsequence model and LCS to ILP reduction#598
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #598 +/- ##
==========================================
+ Coverage 96.61% 96.63% +0.02%
==========================================
Files 218 222 +4
Lines 29352 29745 +393
==========================================
+ Hits 28357 28745 +388
- Misses 995 1000 +5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Add the LongestCommonSubsequence problem model (also addresses #108) and implement the match-pair ILP formulation from Blum et al. (2021) for reducing LCS to Integer Linear Programming. Model: binary selection over shortest string positions, maximizing common subsequence length across all input strings. Reduction: for 2-string case, creates binary variables for character match pairs with assignment and no-crossing constraints. Objective maximizes matched pairs. Includes CLI registration (create, dispatch, alias), unit tests with closed-loop verification, example program, and paper entries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implementation SummaryChanges
Deviations from Plan
Open Questions
|
The reduction test file needs Problem trait for evaluate() calls and Solver trait for BruteForce::find_best() calls. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds the LongestCommonSubsequence (LCS) problem model and implements a reduction from LCS to ILP using the match-pair formulation from Blum et al. (2021). It covers both the model (originally PR #170 whose branch was deleted) and the reduction rule (issue #110) in a single PR.
Changes:
- New
LongestCommonSubsequencemodel with binary config over shortest string, subsequence feasibility checking, and brute-force solvability. - New LCS-to-ILP reduction using match-pair binary variables with one-to-one matching and order-preservation (no-crossing) constraints, scoped to 2-string instances.
- Full integration: CLI dispatch/create/alias, module registration, prelude export, documentation in
reductions.typ, bibliography entries, example, and comprehensive tests.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/models/misc/longest_common_subsequence.rs |
New LCS model: struct, new(), evaluate(), dims(), problem registration |
src/rules/longestcommonsubsequence_ilp.rs |
LCS→ILP reduction: match-pair formulation, constraints, solution extraction |
src/rules/mod.rs |
Register the new reduction module |
src/models/misc/mod.rs |
Register the new model module |
src/models/mod.rs |
Re-export LongestCommonSubsequence |
src/lib.rs |
Add LongestCommonSubsequence to prelude |
problemreductions-cli/src/dispatch.rs |
Add LCS to load_problem() and serialize_any_problem() |
problemreductions-cli/src/commands/create.rs |
Add CLI --strings flag handling for LCS creation |
problemreductions-cli/src/cli.rs |
Add --strings CLI argument and help text |
problemreductions-cli/src/problem_name.rs |
Add "LCS" alias |
src/unit_tests/models/misc/longest_common_subsequence.rs |
14 unit tests for the model |
src/unit_tests/rules/longestcommonsubsequence_ilp.rs |
7 unit tests for the reduction |
examples/reduction_longestcommonsubsequence_to_ilp.rs |
Example with JSON export |
tests/suites/examples.rs |
Register the example test |
docs/paper/reductions.typ |
Problem definition and reduction rule documentation |
docs/paper/references.bib |
Add Maier 1978 and Blum 2021 references |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| strings.len() >= 2, | ||
| "LCS to ILP reduction requires at least 2 strings" |
There was a problem hiding this comment.
The reduction asserts strings.len() >= 2 but only uses strings[0] and strings[1], silently ignoring any additional strings. For 3+ strings, the ILP solution would be incorrect because constraints from the extra strings are not encoded. Since this reduction is scoped to 2 strings only (as stated in the issue), this should assert strings.len() == 2 to prevent silently producing wrong results.
| strings.len() >= 2, | |
| "LCS to ILP reduction requires at least 2 strings" | |
| strings.len() == 2, | |
| "LCS to ILP reduction is defined for exactly 2 strings" |
| } | ||
|
|
||
| crate::declare_variants! { | ||
| LongestCommonSubsequence => "2^total_length", |
There was a problem hiding this comment.
The complexity string "2^total_length" is incorrect. The brute-force search space is determined by dims(), which returns vec![2; shortest_len()] — so the complexity should be 2^min_string_length (where min_string_length corresponds to the shortest string). total_length is the sum of all string lengths, which grossly overestimates the actual search space. You would need to either expose a public min_string_length() getter method for the macro to call, or rename shortest_len() to be public with an appropriate name.
| LongestCommonSubsequence => "2^total_length", | |
| LongestCommonSubsequence => "2^min_string_length", |
| mod longestcommonsubsequence_ilp; | ||
| #[cfg(feature = "ilp-solver")] | ||
| mod ilp_qubo; | ||
| #[cfg(feature = "ilp-solver")] |
There was a problem hiding this comment.
The longestcommonsubsequence_ilp module is placed between ilp_bool_ilp_i32 and ilp_qubo, breaking the alphabetical ordering used by the other modules in this block. It should be placed after knapsack_ilp or before maximumclique_ilp — actually, there's no knapsack_ilp, so it should be placed after ilp_qubo (since "l" > "i") and before maximumclique_ilp (since "l" < "m") to maintain alphabetical order.
| mod longestcommonsubsequence_ilp; | |
| #[cfg(feature = "ilp-solver")] | |
| mod ilp_qubo; | |
| #[cfg(feature = "ilp-solver")] | |
| mod ilp_qubo; | |
| #[cfg(feature = "ilp-solver")] | |
| mod longestcommonsubsequence_ilp; | |
| #[cfg(feature = "ilp-solver")] |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, fix module order - Assert strings.len() == 2 (not >= 2) in LCS-to-ILP reduction to prevent silently ignoring extra strings - Fix complexity from 2^total_length to 2^min_string_length to match actual brute-force search space (dims() uses shortest string) - Fix alphabetical ordering of longestcommonsubsequence_ilp in rules/mod.rs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Inspired by PR #170 which tested this edge case. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Review Pipeline Report
Notes
🤖 Generated by review-pipeline |
Summary
Adds the LongestCommonSubsequence problem model and implements the LCS-to-ILP reduction using the match-pair formulation from Blum et al. (2021).
Since the LCS model (issue #108, PR #170) has a deleted branch, this PR implements both the model and the reduction rule together.
Fixes #110