@@ -77,6 +77,10 @@ def __init__(
77
77
self ._r = reporter
78
78
self ._states : list [State [RT , CT , KT ]] = []
79
79
80
+ # Optimistic backjumping variables
81
+ self ._optimistic_backjumping = True
82
+ self ._save_states : list [State [RT , CT , KT ]] | None = None
83
+
80
84
@property
81
85
def state (self ) -> State [RT , CT , KT ]:
82
86
try :
@@ -324,11 +328,24 @@ def _backjump(self, causes: list[RequirementInformation[RT, CT]]) -> bool:
324
328
except (IndexError , KeyError ):
325
329
raise ResolutionImpossible (causes ) from None
326
330
327
- # Only backjump if the current broken state is
328
- # an incompatible dependency
329
- if name not in incompatible_deps :
331
+ # If optimistic backjumping has switched off only backjump
332
+ # when the current candidate is in the incompatible dependencies
333
+ safe_backjump = name in incompatible_deps
334
+ if not self ._optimistic_backjumping and not safe_backjump :
330
335
break
331
336
337
+ # On the first time a non-regular backjump is done the state
338
+ # is saved so we can restore it later if the resolution fails
339
+ if not safe_backjump and self ._save_states is None :
340
+ self ._save_states = [
341
+ State (
342
+ mapping = s .mapping .copy (),
343
+ criteria = s .criteria .copy (),
344
+ backtrack_causes = s .backtrack_causes [:],
345
+ )
346
+ for s in self ._states
347
+ ]
348
+
332
349
# If the current dependencies and the incompatible dependencies
333
350
# are overlapping then we have found a cause of the incompatibility
334
351
current_dependencies = {
@@ -448,12 +465,32 @@ def resolve(self, requirements: Iterable[RT], max_rounds: int) -> State[RT, CT,
448
465
# Backjump if pinning fails. The backjump process puts us in
449
466
# an unpinned state, so we can work on it in the next round.
450
467
self ._r .resolving_conflicts (causes = causes )
451
- success = self ._backjump (causes )
452
- self .state .backtrack_causes [:] = causes
453
468
454
- # Dead ends everywhere. Give up.
455
- if not success :
456
- raise ResolutionImpossible (self .state .backtrack_causes )
469
+ # If optimistic backjumping fails restore previous state
470
+ try :
471
+ success = self ._backjump (causes )
472
+ except ResolutionImpossible :
473
+ if self ._optimistic_backjumping and self ._save_states :
474
+ failed_optimistic_backjumping = True
475
+ else :
476
+ raise
477
+ else :
478
+ failed_optimistic_backjumping = bool (
479
+ not success
480
+ and self ._optimistic_backjumping
481
+ and self ._save_states
482
+ )
483
+
484
+ if failed_optimistic_backjumping and self ._save_states :
485
+ self ._optimistic_backjumping = False
486
+ self ._states = self ._save_states
487
+ self ._save_states = None
488
+ else :
489
+ self .state .backtrack_causes [:] = causes
490
+
491
+ # Dead ends everywhere. Give up.
492
+ if not success :
493
+ raise ResolutionImpossible (self .state .backtrack_causes )
457
494
else :
458
495
# discard as information sources any invalidated names
459
496
# (unsatisfied names that were previously satisfied)
0 commit comments