FIBERALLOC-62: Add a HiGHS solver backend - #32
Open
monodera wants to merge 8 commits into
Open
Conversation
HighsProblem(LPProblem), reached through highspy, as an open-source alternative to Gurobi that needs no licence. Benchmarked on 22 real target lists from the PFS target uploader: HiGHS finished the same 20 of them Gurobi did, at 1.08x the total runtime, with pointing counts agreeing to within the spread a single solver shows across repeated runs of the same input. Additive throughout. buildProblem() gains solver= and solverOptions=, both defaulting to None, and the existing `gurobi` flag keeps selecting between Gurobi and PuLP whenever solver is not given -- so callers that do not pass it take exactly the path they took before. GurobiProblem and PulpProblem are untouched. Two implementation notes, both measured rather than assumed: Columns are created in one batch. Adding them individually through highspy costs ~50 us each, minutes of overhead on the million-variable problems this module builds; addCols takes the batch at once and measures ~180x faster. buildProblem() creates every variable before its first constraint, so a single deferred flush catches all of them. Solutions are read from one cached vector. Highs.val() recomputes per call at O(numCol) -- 295 us per variable on a 20k-column model, 1083 us on an 80k one -- so reading a solution back variable by variable is quadratic, and needs about an hour on a 500k-column problem whose solve takes 20 s. getSolution() costs a millisecond, once. HiGHS has no lazy-constraint hint, so add_lazy_constraint() adds an ordinary constraint. That costs nothing here: the collision constraints are built up front rather than generated in a callback, and marking them lazy for a backend that does support it left both runtime and objective unchanged on a 1.7M-variable instance. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YKQTw7ZXAZDjgKoaigTGQA
A Gurobi variable has no value to read when the solve failed, so the caller notices at once. HiGHS instead hands back an all-zero column vector for an infeasible or unsolved model, which is indistinguishable from a feasible solution that assigns nothing -- callers reading the solution back would silently treat the failure as an empty assignment. Check getModelStatus() in solve() and raise unless the model was solved to optimality or a limit stopped the search after an incumbent had been found, matching what PulpProblem already does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The comment described a binary/integer split the code does not make: every column is created integral, and a 0/1 range is simply bounded to [0, 1]. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- _cacheSolution's try/except never caught anything: HiGHS returns an all-zero vector rather than raising when there is no solution, and the status check added to solve() is what rules that case out. Drop it and say so, instead of claiming value() will fall back to val(). - varBounds() now reports floats whatever the caller passed in, matching what the Gurobi and PuLP backends return. - Note that `name` is accepted only for interface parity: highspy exposes no model-name API to set it on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts: # ets_fiber_assigner/netflow.py
The prerequisites still described the solver choice as PuLP versus Gurobi. Add HiGHS to that list so the third backend is discoverable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hLuRYpDagE8nVbkGMm96k
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.
https://pfs-jira.naoj.org/jira/browse/FIBERALLOC-62
Adds HiGHS (through
highspy) as a third solver backendfor the netflow MILP, alongside Gurobi and PuLP — an open-source option that
does not require a Gurobi licence.
Changes
HighsProblembackend (ets_fiber_assigner/netflow.py), following thesame
LPProbleminterface asGurobiProblemandPulpProblem. Columns arereserved on
addVar()and created in a single_flush()batch, becauseadding them one at a time through
highspycosts ~50 us each.solve()raises when no solution was produced. Unlike Gurobi, HiGHShands back an all-zero column vector for an infeasible or unsolved model,
which is indistinguishable from a feasible solution that assigns nothing;
the model status is now checked so callers cannot silently read a failure
as an empty assignment.
buildProblem()parameterssolverandsolverOptions.solver="gurobi" | "pulp" | "highs"selects the backend, andsolverOptionspasses options in that backend's own parameter names.When
solverisNonethe existinggurobiflag behaves exactly asbefore, so current callers are unaffected.
highspyis imported lazily insideHighsProblem.__init__, the same waygurobipyandpulpare, so it is only needed when that backend is used andno new hard dependency is introduced.
Benchmark
Benchmarked on 22 real target lists: HiGHS finished the same 20 that Gurobi
did, at 1.08x the total runtime, with pointing counts agreeing to within the
spread a single solver shows across repeated runs of the same input.
Notes
separately on release.
values with
> 0, which is not tolerance-safe for the near-integral doublesa MIP solver returns. The demo scripts are considered obsolete, so no fix is
planned for now.
🤖 Generated with Claude Code