Skip to content

Fix QUBO local-minimum polish#32

Open
bernalde wants to merge 2 commits into
mainfrom
fix/issue-19-local-minima
Open

Fix QUBO local-minimum polish#32
bernalde wants to merge 2 commits into
mainfrom
fix/issue-19-local-minima

Conversation

@bernalde
Copy link
Copy Markdown
Member

@bernalde bernalde commented May 15, 2026

Summary

  • Add a bounded branch-and-bound polish step for QUBO matrix solves so DMRG samples that land in local minima can be improved to a better bitstring.
  • Expose polish controls through direct minimize keywords and JuMP raw optimizer attributes: polish, polish_max_variables, and polish_time_limit.
  • Add regression coverage for the sparse 36-variable QUBO from issue Getting stuck on local minima #19, verifying the polish step recovers objective 11.7099.

Tests run

  • julia +1.12 --project=. -e 'using Test, Random, LinearAlgebra, TenSolver; include("test/utils.jl"); include("test/qubo.jl")'
    • Result: passed, QUBO Correctness | 78/78.
  • julia +1.12 --project=. -e 'using Pkg; Pkg.test()'
    • Result: passed, TenSolver tests passed.
  • git diff --check HEAD
    • Result: passed, no whitespace errors.

Notes

  • julia +1.10 --project=. -e 'using Pkg; Pkg.instantiate()' could not be run successfully with the checked-in manifest because the manifest was resolved with Julia 1.12.6 and Julia 1.10 reported missing OpenSSL_jll.
  • No separate lint, type-check, or formatting commands are documented in the repository.

Closes #19

@bernalde bernalde force-pushed the fix/issue-19-local-minima branch from c650b7d to 8ba41c9 Compare May 15, 2026 00:19
Copy link
Copy Markdown
Member Author

@bernalde bernalde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found two blocking issues: the new polish phase is not bounded by the public solve time limit, and the issue regression bypasses the public solve path it is meant to protect. I would not merge this until those are addressed.

Comment thread src/solver.jl
x = sample(dist)
optimal = obj(x)

if !isnothing(postprocess)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: time_limit is only checked inside the DMRG loop, then polish runs with its own default 1s budget even if the caller's solve budget is already exhausted. This also affects the JuMP path because MOI.TimeLimitSec() maps to time_limit in QUBODrivers.sample, but the default polish_time_limit still runs afterwards. Users relying on time limits can now exceed them by up to the polish budget on every <=36 variable QUBO. Please cap the polish budget by the remaining solve time for finite time_limit, or skip polish when no time remains, and add a small test covering that interaction.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in b59237b. Polish now receives the remaining overall solve budget and is skipped when no time remains; polish_time_limit is only an additional cap. Added test/qubo.jl coverage in polish respects solve time limit.

Comment thread test/qubo.jl
c = 641.2245
x0 = zeros(Int, 36)

polished = TenSolver._branch_bound_qubo(Q, l, c, x0, dot(x0, Q, x0) + dot(l, x0) + c)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: This regression test calls the private helper directly, so it would still pass if minimize stopped wiring postprocess, if polish default changed, or if the returned Solution did not sample the polished bitstring. Issue #19 is about the public solve path getting stuck in local minima. Please add a public integration test that exercises TenSolver.minimize(Q, l, c; polish=true, ...) and asserts both the returned objective and the sampled solution objective. The helper-level test can stay as unit coverage.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in b59237b. The issue #19 regression now also exercises TenSolver.minimize(Q, l, c; polish=true, ...) and checks both the returned objective threshold and the sampled bitstring objective; the helper-level coverage remains.

Comment thread src/solver.jl Outdated
The variance test determines whether DMRG converged to an eigenstate (not necessarily the ground state),
but is expensive to calculate.
- `polish :: Bool` - Run bounded branch-and-bound postprocessing for QUBO matrix inputs. Defaults to `true`.
- `polish_max_variables :: Int` - Maximum number of variables eligible for exact polishing. Defaults to `36`.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nonblocking: exact polishing is misleading while polish_time_limit can expire; _branch_bound_qubo returns the best improved incumbent found so far without indicating that optimality was proven. Please describe this as bounded or best-effort polishing unless the search completes, or expose proof status if the exactness guarantee matters.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in b59237b. The docstring and regression test wording now describe this as bounded polishing, and the docs state that the step returns the best improved incumbent found within the budget.

@bernalde
Copy link
Copy Markdown
Member Author

Follow-up pushed: b59237b (Address QUBO polish review feedback (#19)).

Main changes made:

  • Capped QUBO polish by the remaining overall solve time_limit; if no solve time remains, polish is skipped.
  • Kept polish_time_limit as an additional cap inside the remaining solve budget.
  • Added a regression test that verifies postprocessing is not called after the solve time budget is exhausted.
  • Added a public TenSolver.minimize(Q, l, c; polish=true, ...) issue Getting stuck on local minima #19 regression that checks the returned objective is at least as good as the reported 11.7099 value and that the sampled bitstring objective matches E.
  • Updated polish documentation/test wording from “exact” to “bounded” because time-limited polish can return the best improved incumbent found so far.

Tests run and results:

  • julia +1.12 --project=. -e 'using Test, Random, LinearAlgebra, TenSolver; include("test/utils.jl"); include("test/qubo.jl")'
    • Passed: QUBO Correctness | 81/81.
  • julia +1.12 --project=. -e 'using Pkg; Pkg.test()'
    • Passed: TenSolver tests passed.
  • julia +1.12 --project=docs/ docs/make.jl
    • Passed; local Documenter deployment was skipped because no CI environment was detected.
  • git diff --check
    • Passed.

Comments intentionally not addressed:

  • None.

Remaining risks or follow-up items:

  • Polish remains bounded/best-effort. If the branch-and-bound search exhausts its time budget, it may improve the incumbent without proving global optimality.

@bernalde bernalde requested a review from iagoleal May 15, 2026 20:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Getting stuck on local minima

1 participant