-
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MAINT,ENH: linprog
, highs
and callback mechanisms
#19451
Comments
All non-HiGHS methods are already deprecated. They produce a warning stating that they "will be removed in SciPy 1.11.0". We missed that deadline only because we didn't think we should remove these methods without a compatible callback interface. If we aren't going to provide a callback interface that is compatible with what they have offered, we will probably need to restart the deprecation cycle or continue to offer one of these methods.
Why is there necessarily a slowdown? It is possible to design things such that there is essentially zero overhead if a callback function does not need to be called.
I thought we are the reason they were adding a callback interface to begin with. Is that not right?
We can add that as we have done for |
@HaoZeke's comment seems to be in reference to the amount of data being passed around, i.e., copying data to and from structs is easy but does have some overhead cost and we'd need to justify that cost.
We are probably on their map, but they seem to have other drivers and I don't see any evidence that we had a lot of input into their design other than expressing what we were currently doing in the legacy SciPy linprog implementations. (I haven't been able to follow very closely though, so this could be wrong).
Reading their callback documentation, I'm wondering if it's possible to emulate per-iteration callbacks for simplex and interior point via the Simplex and IPM Interrupt callbacks until the old-style callbacks can be deprecated fully. The interrupt callbacks seem to be guaranteed to be run every iteration. I have this in mind, e.g., Simplex:
There are two assumptions above:
A good rule of thumb seems to be that if you do what Gurobi does, everything else is at least pretty close to that. So might be worth taking a look at that documentation is and seeing what differences there are. |
From 30000 feet and without knowing details, I wonder what is the value of having |
I had this confusion initially as well @ev-br, and the scope of where SciPy's linprog fits into the ecosystem is discussed by @fuglede in #15915 (comment) which I think is important enough to be reproduced here for context:
My understanding from that is that in the hierarchy there is:
Which seems reasonable. There are other reasons to have SciPy's Another thing (but maybe I am not the best person to talk about this) is that it seems the callbacks were originally a special feature of the |
Yup, also currently there is one data structure which is being used for all the callback kinds, the slowdown here is the fact that say, a callback meant to only interrupt the solver shouldn't need to do the (potentially expensive) operation of fillin a struct which may or may not even be used for logging (depending on if the logging callback is active). I might be wrong, but generally, large data transfer / computation purely for logging is almost always only for development / small problems, eventually for production cases it becomes difficult to interpret anyway.
There are other drivers, but I think the HiGHs devs are very open to reasonable changes, as long as they are also likely to be of use in production systems (i.e. don't slow down the solver). This was why for example, per-iteration callbacks were a non-starter (I have since opened a discussion regarding if we might include a compile time guarded path just for SciPy but it would just increase maintainer burdens / might cause debugging issues).
In general HiGHs consideres thread safety, so if there aren't any guarantees yet they'd be open to fixing that. However, AFAIK think the in-progress simplex solution is not part of the parent
Yeah it seems like most of the linear optimization literature / software have different paradigms but generally converge (across C++ / Julia / Java / Matlab / other) implementations to Gurobi as a baseline.. |
From 10,000m, using our callback mechanism, HiGHS could add a per-iteration callback to simplex with zero overhead for those who don't want. However, even if it were activated, the only information it would make available is execution time and primal or dual objective function value, depending on whether primal or dual simplex is being used. Generally it would be a call from the dual simplex algorithm applied to a presolved LP with perturbed costs, so it wouldn't even know the primal objective value without the unacceptable overhead of computing it. And, it would be at a point that is, at best, only dual feasible. So, if the incumbent solution in HiGHS were converted into the corresponding solution for the original LP, it would normally only be a dual solution, and who amongst your users would interpret that? The primal solution that is known in dual simplex - like the dual solution known in primal simplex - is infeasible until optimality. Surely the current per-factorization callbacks in simplex come frequently enough for any sane user In short I'd ditch per-iteration callbacks, and explain the futility of them to any user who objects. Generally, we are prepared to add things that are useful to major users, but nothing that would impact meaningfully on performance. We are adding callbacks because (1) they are things that optimization solvers offer and (2) because they are required by an upcoming major user of HiGHS.
I do wonder at the value of these advantages when the underlying linprog solver is so much slower than HiGHS. I find it baffling that anything in the SciPy - HiGHS interface could ever be as remotely expensive as solving an LP or MIP. I'm also puzzled by the reference to converting inequality constraints to equalities. HiGHS handles general boxed constraints so, whatever forms of constraints SciPy uses can be communicated without modification. Yes, linprog may be "zero overhead" but I'd be amazed if the HiGHS overhead compares remotely with its superior performance. |
Python modeling interfaces can be very slow (which is why I'm not a huge fan of them, but data scientists sure seem to be). IIRC, Google's or-tools is a big offender where most of the time can be spent building models using classes and abstractions before anything is converted to actual coefficients and constraint matrices and the solver invoked. SciPy is different in the Python library ecosystem in this regard by providing light, MATLAB-linprog-like constraint abstractions. I think the comment about converting inequality constraints to equalities is misleading and should instead point to the (trivial but annoying) changing of constraint formulation from the SciPy/MATLAB-style +1 for relaxing the requirement of SciPy linprog simplex callbacks to run every iteration and instead at a factorization points. |
Interesting comments on speed of modelling languages, but how is this relevant to HiGHS: we just deal in finalised matrices - or are you referring to building models by calls to HiGHS to add individual variables and constraints? JuMP seems to be efficient. Is this because it's written in Julia?
If it's the concatenation of three matrices of constraint coefficients into one that's irritating - surely it's not meaningfully expensive - then I can add an alternative method for passing an LP that has these restrictions on the general boxed constraints and then do the concatenation at negligible cost in C++ Use of general boxed constraints is more efficient, particularly when modellers want distinct, finite lower and upper bounds. With the SciPy/MATLAB style, an extra matrix row is required. |
(At the risk of getting distracted from the core issue at hand...)
I don't think there's anything relevant to HiGHS here, just a comment on modeling overhead incurred by some Python libraries that either integrate HiGHS or use other solvers. JuMP could be more efficient at runtime here due its compilation steps (able to statically transform a modeling representation into finalized matrices). I just pulled up a presentation we gave back in 2021 comparing availability and performance of LP solvers in the Python ecosystem -- it looks like just OR-Tools had the model building problem which added seconds of runtime to a benchmark run against various sizes of dense constraint matrices: I can't comment on if it's an issue anymore in OR-Tools and is likely beside the point in this issue -- presented only for your curiosity :) A counter argument here would be that modeling overhead might be worth it if it's useful to the user and you're not constantly rebuilding the model |
SciPy callbacks
Associated PR: #19420 (WIP).
Some comments on additions required for
scipy
. In some sense the callbacks inhighs
are more functional (they allow, e.g. interrupts) than those ofscipy
.scipy
only needs to have theOptimizeResult
available for consumption by thecallback
, that is, it is essentially alogging
callback.In general, an
OptimizeResult
has:As will be seen however, not all of these are set (both in the documentation and implementation).
simplex
callbacksIn the implementation, we see:
revised_simplex
As seen in
_linprog_rs.py
The implementation:
interior-point
Implementation:
Mapping to
highspy
highspy
uses theHighsCallbackDataOut
which has:Which means that (bold entries are not present):
false
without much trouble (e.g._rs
and_ip
)ip
sets this to 1 alwaysHighsCallbackDataOut
)However, to add things which are only ever going to be used for logging seems like a pointless slowdown though. Additionally, it seems like (#9536) the design of
linprog
callbacks could in general do with some more discussion.Alternatives
There is a similar issue open over at HiGHS (ERGO-Code/HiGHS#911), but as such, from a solver perspective, the existing implementation seems to be pretty good.
If it is acceptable,
highs
can either include a SciPy compatible (guarded by a compile flag) set of callbacks building on what they have, or we could maintain a set in our copy of HiGHs.Possible next steps
The choices are:
highspy
style callbacksAnything in between requires changes to
highs
. I believe in part this issue is linked to "what is the scop of SciPy's linear solvers" and so #15915 (comment), where @fuglede describes his usage of SciPy in the context of also havinghighspy
/ PuLP / PyOMO etc. is of use.Seeking comments from anyone using
linprog
but also maybe @rgommers , @mckib2 , @mdhaber .Personally, I think it would be best to transparently pass callback functionality through to
highspy
, as that has the lowest maintenance burden to additional feature ratio. Unless it is in wide use, then, I would suggest first deprecating the existing methods #15707 (non-HiGHs) then adding backhighspy
compatible callbacks. Or we could remove existing methods while introducinghighspy
compatible callbacks.The text was updated successfully, but these errors were encountered: