Skip to content

Commit

Permalink
Allow backtracking to before a specific package (#38)
Browse files Browse the repository at this point in the history
This allows discarding a previously made decision if it turned out to be
a bad decision, even if all options with this decision have not yet been
rejected.

We allow attempting to backtrack on packages that were not decided yet
to avoid the caller from making the duplicative check.

astral-sh/uv#8157
  • Loading branch information
konstin committed Jan 9, 2025
1 parent e9be71e commit fddb1a3
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 1 deletion.
18 changes: 17 additions & 1 deletion src/internal/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,8 @@ impl<DP: DependencyProvider> State<DP> {
}
}

/// Backtracking.
/// After a conflict occurred, backtrack the partial solution to a given decision level, and add
/// the incompatibility if it was new.
fn backtrack(
&mut self,
incompat: IncompDpId<DP>,
Expand All @@ -305,6 +306,21 @@ impl<DP: DependencyProvider> State<DP> {
}
}

/// Manually backtrack before the given package was selected.
///
/// This can be used to switch the order of packages if the previous prioritization was bad.
///
/// Returns the number of the decisions that were backtracked, or `None` if the package was not
/// decided on yet.
pub fn backtrack_package(&mut self, package: Id<DP::P>) -> Option<u32> {
let base_decision_level = self.partial_solution.current_decision_level();
let new_decision_level = self.partial_solution.backtrack_package(package).ok()?;
// Remove contradicted incompatibilities that depend on decisions we just backtracked away.
self.contradicted_incompatibilities
.retain(|_, dl| *dl <= new_decision_level);
Some(base_decision_level.0 - new_decision_level.0)
}

/// Add this incompatibility into the set of all incompatibilities.
///
/// PubGrub collapses identical dependencies from adjacent package versions
Expand Down
23 changes: 23 additions & 0 deletions src/internal/partial_solution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::cmp::Reverse;
use std::fmt::{Debug, Display};
use std::hash::BuildHasherDefault;

use log::debug;
use priority_queue::PriorityQueue;
use rustc_hash::FxHasher;

Expand Down Expand Up @@ -451,6 +452,28 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
self.has_ever_backtracked = true;
}

/// Backtrack the partial solution before a particular package was selected.
///
/// This can be used to switch the order of packages if the previous prioritization was bad.
///
/// Returns the new decision level on success and an error if the package was not decided on
/// yet.
pub(crate) fn backtrack_package(&mut self, package: Id<DP::P>) -> Result<DecisionLevel, ()> {
let Some(decision_level) = self.package_assignments.get_index_of(&package) else {
return Err(());
};
let decision_level = DecisionLevel(decision_level as u32);
if decision_level > self.current_decision_level {
return Err(());
}
debug!(
"Package backtracking ot decision level {}",
decision_level.0
);
self.backtrack(decision_level);
Ok(decision_level)
}

/// Add a package version as decision if none of its dependencies conflicts with the partial
/// solution.
///
Expand Down

0 comments on commit fddb1a3

Please sign in to comment.