Redefine BMF as exact factorization; fix BicliqueCover semantics; add BMF → BicliqueCover#1056
Open
Redefine BMF as exact factorization; fix BicliqueCover semantics; add BMF → BicliqueCover#1056
Conversation
The Rust BMF model previously minimized Hamming distance between A and B*C
(approximate factorization). This redefines it to the classical exact form:
feasible iff B*C = A, infeasible otherwise, with objective = total number of
1s in B and C (Monson-Pullman-Rees 1995).
Model changes (src/models/algebraic/bmf.rs):
- evaluate() returns Min(None) when B*C != A, else Min(Some(|B|_1 + |C|_1))
- Add total_factor_size() helper
- Fix canonical example: the previous optimal_config was not actually exact
(verified by hand: Hamming distance 2, not 0). New config is a true exact
factorization with total factor size 8.
BMF -> ILP reduction (src/rules/bmf_ilp.rs):
- Drop error variables e_{i,j} and their |A - w| inequalities
- Pin w_{i,j} = A_{i,j} (exact reconstruction)
- Objective changes from sum e_{i,j} to sum b_{i,r} + sum c_{r,j}
- Overhead shrinks: num_vars drops by m*n, num_constraints drops by m*n
BMF -> BicliqueCover reduction intentionally NOT added: the codebase's
BicliqueCover is the OR-cover variant (allows bicliques to cover non-edges),
which is strictly weaker than exact BMF. A closed-loop reduction would
extract invalid (non-exact) BMF configs. The classical equivalence with
biclique cover requires the stronger "sub-biclique-of-G" definition that
BicliqueCover does not currently enforce.
Julia parity fixture for BMF is removed (tests/data/jl/bmf.json) — the old
file encoded Hamming-distance semantics that no longer apply. Related
generator code in scripts/jl/generate_testdata.jl is commented out with a
note explaining the semantics change.
Paper (docs/paper/reductions.typ) is updated: BMF problem definition now
states exact factorization and |B|_1 + |C|_1 objective; BMF -> ILP rule
replaces the error-variable construction with the equality-constrained
formulation. Mentions the Boolean rank / biclique cover number equivalence
in the Extra Remark section.
Two incidental cargo fmt cleanups are included in
partition_into_paths_of_length_2.rs, maximumindependentset_triangular.rs,
and reduction_graph.rs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1056 +/- ##
=======================================
Coverage 97.92% 97.92%
=======================================
Files 964 966 +2
Lines 99872 99933 +61
=======================================
+ Hits 97796 97857 +61
Misses 2076 2076 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
BicliqueCover's `is_valid_cover` previously checked only that every graph
edge is covered by some biclique — it did not forbid a biclique from
containing a non-edge of G. That's the OR-cover / cover-by-rectangles
variant, which disagrees with the docstring (and with the textbook
definition). It's also why the earlier attempt at BMF → BicliqueCover was
unsound: a 2×2 identity at rank 1 was infeasible for exact BMF but
"feasible" for OR-cover via the K_{2,2} pseudo-biclique.
Changes:
- `BicliqueCover::is_valid_cover` now also requires every biclique's
`L_b × R_b` to be a subset of E(G). This aligns implementation with the
docstring and with the paper's constraint `x_{l,b} + y_{r,b} ≤ 1 for
non-edges` (which the paper already described but the code didn't
enforce).
- `BicliqueCover → ILP` adds the `x + y ≤ 1` constraint for every non-edge
and every biclique, matching the paper.
- `test_biclique_problem` updated: `[1,1,1,1]` on a graph with only edge
(0,0) now evaluates to `Min(None)` (it's a pseudo-biclique covering
non-edges).
- Canonical BicliqueCover example switched from the OR-cover optimum
(total size 5) to the correct sub-biclique optimum (total size 6),
matching the paper's worked example exactly.
- Julia parity fixture `tests/data/jl/biclique_cover.json` and its Rust
parity test are dropped; the Julia package (Go6Cf) has the same
OR-cover bug so its fixture would disagree with the fixed Rust
semantics. Generator note explains the divergence.
- New `BMF → BicliqueCover` reduction: matrix-to-biadjacency, rank
passthrough, with `extract_solution` transposing the right-vertex half
from vertex-major to biclique-row-major. Verified by closed-loop tests
on all-ones rank-1, identity rank-2, and an infeasibility test on
identity rank-1 (both problems now infeasible, consistent).
- Paper updated: BicliqueCover problem-def now states the sub-biclique
requirement explicitly; new `reduction-rule("BMF", "BicliqueCover")`
entry added with the Monson–Pullman–Rees equivalence and the variable
layout transpose.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two aligned changes; the second only works after the first.
Change 1 — Redefine
BMFfrom approximate factorization (minimize Hamming distance, every config feasible) to exact factorization: infeasible whenB ⊙ C ≠ A, else minimize|B|_1 + |C|_1(total factor size). Matches the classical problem (Monson–Pullman–Rees 1995) and restores the Boolean-rank interpretation.BMF → ILPto the equality-constrained formulation: drop error variablese_{i,j}, pinw_{i,j} = A_{i,j}, change objective tomin Σ b_{i,r} + Σ c_{r,j}. Overhead shrinks bym·nvariables andm·nconstraints.optimal_configwas not actually an exact factorization (verified by hand: Hamming distance 2). Replaced with a genuine exact factorization that makestest_bmf_paper_exampleandmodel_specs_are_optimalconsistent.Change 2 — Fix
BicliqueCoversemantics to the classical sub-biclique form, then add the trivialBMF → BicliqueCoverreduction.is_valid_covernow also requires every biclique'sL_b × R_b ⊆ E(G). Previously the code only checked edge coverage — the OR-cover variant — which disagreed with the struct's docstring and with the paper's ILP constraintx_{l,b} + y_{r,b} ≤ 1for non-edges (the paper was already describing the stronger form; the code was underconstrained).BicliqueCover → ILPnow emits the matching non-edge constraint.BicliqueCoverexample switched from the OR-cover optimum (total size 5) to the correct sub-biclique optimum (total size 6), matching the paper's worked example.BMF → BicliqueCoverreduction: matrix-to-biadjacency + rank passthrough, withextract_solutiontransposing the right-vertex half from vertex-major to biclique-row-major. Trivial under the fixed semantics.BicliqueCoverproblem-def now states the sub-biclique requirement explicitly; newreduction-rule("BMF", "BicliqueCover")entry added.Julia parity fixtures dropped for both
BMF(old Hamming-distance semantics) andBicliqueCover(JuliaProblemReductionspackage has the same OR-cover bug — itsis_bicliqueonly checks non-emptiness). The corresponding Rust parity tests are removed and the Julia generator has notes explaining the divergence. The upstream Julia package will need the same fix separately.Test plan
cargo build --libcleancargo clippy --all-targets -- -D warningscleancargo test --lib— 4951 passcargo test --lib --features example-db— 5050 pass (includesmodel_specs_are_self_consistentandmodel_specs_are_optimalfor the updated canonical specs)cargo test --test main— 75 integration tests passmake export-schemasandmake paperrebuild cleanlypred path BMF BicliqueCoverreturns a 1-hop path;pred create --example BMF→pred evaluategivesMin(8)for the exact config andMin(None)for infeasible configs🤖 Generated with Claude Code