[ENH] set_cover Python: expose the lower bound accessors directly - #5277
Open
jg-codes wants to merge 1 commit into
Open
[ENH] set_cover Python: expose the lower bound accessors directly#5277jg-codes wants to merge 1 commit into
jg-codes wants to merge 1 commit into
Conversation
DualAscentOptimizer computes a dual lower bound and stores it via inv()->ReportLowerBound() (set_cover_heuristics.cc:1276, :1314). SetCoverInvariant exposes it in C++ as LowerBound() and CostOrLowerBound(), neither of which is bound in the pybind11 wrapper. The value is reachable from Python today, just indirectly: ExportSolutionAsProto() serializes it into SetCoverSolutionResponse.cost_lower_bound (set_cover_invariant.cc:536), and that binding exists. But reading a scalar today means serializing the whole solution proto, which is awkward when the caller wants an optimality gap next to inv.cost(). Binds LowerBound(), CostOrLowerBound() and is_cost_consistent(). The last one is needed because ReportLowerBound() is called with is_cost_consistent=false, so a caller has to be able to tell whether the invariant currently holds a feasible solution or a relaxation, which is exactly what CostOrLowerBound() switches on. Adds two tests. Built and tested on Linux (Bazel 8.7.0) against main at d9c0910: bazel test //ortools/set_cover/python:set_cover_test passes.
Author
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.
Edited 2026-09-05: shortened, the cross-reference to #5122 dropped so this PR stands on its own, and the limits of the existing workaround spelled out. Code unchanged. Re-built and re-run against
main@5a1b660on 2026-09-05, and provided cost estimate of the existing workaround.What
SetCoverInvarianthasLowerBound(),CostOrLowerBound()andis_cost_consistent()in C++. None of the three are bound inortools/set_cover/python/set_cover.cc. This adds them next to the existingcostbinding (plus two tests).DualAscentOptimizerwrites its dual bound onto the invariant throughReportLowerBound(). From Python, the only way to read it today is:That path has two problems.
i) It walks every subset in the model and copies the selected ones into a proto in order to hand back one double, so costing a full pass over the model every time by reading a gap inside. Measured on
main@5a1b660with this PR applied, one read costs about 28 ns per subset: 0.8 ms at 3,000 subsets (scpb1), 28 ms at 1,000,000 subsets, against 0.9 µs forinv.lower_bound(). AndExportSolutionAsProto()writes the rawlower_bound_, whileSetCoverSolutionResponsehas no field for this consistency flag.A Python caller therefore cannot tell whether that number is a live bound or a stale
one, and
CostOrLowerBound(), which branches on that flag, cannot bereconstructed from Python.
With the bindings:
is_cost_consistent()is included for the same reason:ReportLowerBound()is called withis_cost_consistent=false. After aDualAscentOptimizerrun onscpb1, for example,cost()is 75,lower_bound()is 45,is_cost_consistent()isFalseandcost_or_lower_bound()is 45; the proto shows only the 75 and the 45.Three
.def()lines and two tests. No new dependency, no build change.Verification
bazel test //ortools/set_cover/python:set_cover_testpasses on Linux (Bazel 8.7.0,x86_64) against
main@5a1b660(2026-09-05).clang-formatclean against the repo.clang-format;blackclean at the file's existing line width.CLA signed. Drafted with Claude; built, run and reviewed by me.