-
Notifications
You must be signed in to change notification settings - Fork 34
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
Back jump doesn't do well in some cases #180
Comments
Yeah, the exit is probably too aggressive, but there are definitely cases that incorrectly reach resolution impossible without it. I have at least a few real world examples to try against if you have an idea how to loosen this without causing incorrect resolutions. |
I think we previously proposed to save the states first, and if no state that can jump is found, restore the states and fallback to backtrack. |
Btw, I have an intuition that if the backtrack causes are disjoint, i.e every cause is incompatible with some other cause (or set of causes), resolvelib could aggressively backjump. But currently the provider has no way to indicate to the resolver disjointness. I will experiment this week with disjointness and aggressively backjumping, to see if the intuition is correct. |
Yeah, it wasn't very elegant though, for example it made complicated impossible resolutions much slower. |
My understanding is the way backjumping was originally implemented in resolvelib did not actually meet the prerequisites for backjumping to be logically sound. In particular a resolvelib "cause" is distinct from what a "cause" might be in satisfiability theory, for example you might end up with "causes" in resolvelib that have the constraints I was hoping to introduce a new API that allowed the provider to filter these causes to only the logical ones, but I realized there's another case that I wasn't considering (#171 (comment)) and further I'm also concerned that the "mathematics" of pre-releases will have an unknown impact on trying to implement classical satisfiability theory (e.g. https://discuss.python.org/t/proposal-intersect-and-disjoint-operations-for-python-version-specifiers/71888/1). All this is a long winded way to say I'm moving towards the opinion of introducing a save state. My thoughts are:
I think we would call this "optimistic backjumping", we backjump on the assumption that resolvelib causes are close enough to logical satisfiability causes, and we revert in the face of resolution impossible. I am going to work on making a PR and running it against scenarios in https://github.com/notatallshaw/Pip-Resolution-Scenarios-and-Benchmarks to see what the performance wins are. |
I made a branch for pip that adds a commit for optimistic backjumping on top of resolvelib 1.1 (pypa/pip#13001) and then ran scenarios to compare them. It comes with big wins and a big loss, here are the statistics comparing the resolvelib 1.1 branch to the optimistic backjumping branch: Output from comparison using tools from Pip-Resolution-Scenarios-and-Benchmarks:
Some details to note, my tooling stops resolution at ~15k rounds as so far I haven't found a resolution that completes between 15k and 200k (pip's max default) rounds. There were no resolution differences for either popular packages or big packages, all the resolution differences were related to problematic resolutions. Generally performance was much better, in-particular boto3-urllib3-transient and kauldron-docs went from resolution too deep to quickly solvable which is the best win. And sphinx went from resolution too deep to build failure, which I consider a win because it gives the user information to apply lower bounds. However, apache-airflow-google-cloud went from quickly completing to resolution too deep, which I consider to be the worst failure, this is the same error that the current pip 24.3 produces, and resolvelib 1.1 was going to solve this. I suspect that optimistic backjumping has skipped over an important branch in the resolution graph and even with optimistic backjumping it can not reach resolution impossible before the 15k rounds in my tooling (nor even the 200k rounds in pip 24.3). I have some bug fixes and further optimizations to pip I want to test against to see if this improves the "apache-airflow-google-cloud" situation at all, but due to obligations I don't think I will have anymore time to dedicate to this in 2024. Currently this leaves me on the fence about optimistic backjumping. |
Here are the package set to be resolved
Assume the resolver resolves packages from top to down, when resolving D, the current pins are:
Because
B@2
is pinned which constrainsD==2
, we can't find a compatible version ofD
that is also happy withA@1
, so conflicts happen and we back jump and pop the last pinned packageC
, which is totally unrelated to the incompatibilities.However, due to the line in
backjump
implementation:resolvelib/src/resolvelib/resolvers/resolution.py
Lines 327 to 330 in 51dbc92
The backjump quits very early and fallbacks to backtrack. It would be extremely slow if traversing C is super expensive. But it's obvious we should jump over to the state of
B
and try other versions. My question is, does the quit condition make sense in any case I am not aware of or should we fix it?@notatallshaw
To be concrete,
A: h11, B: httpcore, C: Pillow, D: httpx
The text was updated successfully, but these errors were encountered: