Skip to content

Commit

Permalink
perf: remove not_intersected_yet (#87)
Browse files Browse the repository at this point in the history
  • Loading branch information
Eh2406 authored May 11, 2021
1 parent ee7b084 commit 9b2518e
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 75 deletions.
54 changes: 28 additions & 26 deletions src/internal/incompatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,32 +143,6 @@ impl<P: Package, V: Version> Incompatibility<P, V> {
}
}

/// CF definition of Relation enum.
pub fn relation(&self, mut terms: impl FnMut(&P) -> Option<Term<V>>) -> Relation<P> {
let mut relation = Relation::Satisfied;
for (package, incompat_term) in self.package_terms.iter() {
match terms(package).map(|term| incompat_term.relation_with(&term)) {
Some(term::Relation::Satisfied) => {}
Some(term::Relation::Contradicted) => {
return Relation::Contradicted(package.clone());
}
None | Some(term::Relation::Inconclusive) => {
// If a package is not present, the intersection is the same as [Term::any].
// According to the rules of satisfactions, the relation would be inconclusive.
// It could also be satisfied if the incompatibility term was also [Term::any],
// but we systematically remove those from incompatibilities
// so we're safe on that front.
if relation == Relation::Satisfied {
relation = Relation::AlmostSatisfied(package.clone());
} else {
relation = Relation::Inconclusive;
}
}
}
}
relation
}

/// Check if an incompatibility should mark the end of the algorithm
/// because it satisfies the root package.
pub fn is_terminal(&self, root_package: &P, root_version: &V) -> bool {
Expand Down Expand Up @@ -246,6 +220,34 @@ impl<P: Package, V: Version> Incompatibility<P, V> {
}
}

impl<'a, P: Package, V: Version + 'a> Incompatibility<P, V> {
/// CF definition of Relation enum.
pub fn relation(&self, terms: impl Fn(&P) -> Option<&'a Term<V>>) -> Relation<P> {
let mut relation = Relation::Satisfied;
for (package, incompat_term) in self.package_terms.iter() {
match terms(package).map(|term| incompat_term.relation_with(&term)) {
Some(term::Relation::Satisfied) => {}
Some(term::Relation::Contradicted) => {
return Relation::Contradicted(package.clone());
}
None | Some(term::Relation::Inconclusive) => {
// If a package is not present, the intersection is the same as [Term::any].
// According to the rules of satisfactions, the relation would be inconclusive.
// It could also be satisfied if the incompatibility term was also [Term::any],
// but we systematically remove those from incompatibilities
// so we're safe on that front.
if relation == Relation::Satisfied {
relation = Relation::AlmostSatisfied(package.clone());
} else {
relation = Relation::Inconclusive;
}
}
}
}
relation
}
}

impl<P: Package, V: Version> fmt::Display for Incompatibility<P, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
Expand Down
58 changes: 15 additions & 43 deletions src/internal/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@ pub struct Memory<P: Package, V: Version> {
#[derive(Clone)]
enum PackageAssignments<V: Version> {
Decision((V, Term<V>)),
Derivations {
intersected: Term<V>,
not_intersected_yet: Vec<Term<V>>,
},
Derivations(Term<V>),
}

impl<P: Package, V: Version> Memory<P, V> {
Expand All @@ -46,9 +43,9 @@ impl<P: Package, V: Version> Memory<P, V> {
}

/// Retrieve intersection of terms in memory related to package.
pub fn term_intersection_for_package(&mut self, package: &P) -> Option<&Term<V>> {
pub fn term_intersection_for_package(&self, package: &P) -> Option<&Term<V>> {
self.assignments
.get_mut(package)
.get(package)
.map(|pa| pa.assignment_intersection())
}

Expand Down Expand Up @@ -93,18 +90,12 @@ impl<P: Package, V: Version> Memory<P, V> {
Entry::Occupied(mut o) => match o.get_mut() {
// Check that add_derivation is never called in the wrong context.
PackageAssignments::Decision(_) => debug_assert!(false),
PackageAssignments::Derivations {
intersected: _,
not_intersected_yet,
} => {
not_intersected_yet.push(term);
PackageAssignments::Derivations(t) => {
*t = t.intersection(&term);
}
},
Entry::Vacant(v) => {
v.insert(PackageAssignments::Derivations {
intersected: term,
not_intersected_yet: Vec::new(),
});
v.insert(PackageAssignments::Derivations(term));
}
}
}
Expand All @@ -115,9 +106,9 @@ impl<P: Package, V: Version> Memory<P, V> {
/// selected version (no "decision")
/// and if it contains at least one positive derivation term
/// in the partial solution.
pub fn potential_packages(&mut self) -> impl Iterator<Item = (&P, &Range<V>)> {
pub fn potential_packages(&self) -> impl Iterator<Item = (&P, &Range<V>)> {
self.assignments
.iter_mut()
.iter()
.filter_map(|(p, pa)| pa.potential_package_filter(p))
}

Expand All @@ -131,13 +122,8 @@ impl<P: Package, V: Version> Memory<P, V> {
PackageAssignments::Decision((v, _)) => {
solution.insert(p.clone(), v.clone());
}
PackageAssignments::Derivations {
intersected,
not_intersected_yet,
} => {
if intersected.is_positive()
|| not_intersected_yet.iter().any(|t| t.is_positive())
{
PackageAssignments::Derivations(intersected) => {
if intersected.is_positive() {
return None;
}
}
Expand All @@ -149,20 +135,10 @@ impl<P: Package, V: Version> Memory<P, V> {

impl<V: Version> PackageAssignments<V> {
/// Returns intersection of all assignments (decision included).
/// Mutates itself to store the intersection result.
fn assignment_intersection(&mut self) -> &Term<V> {
fn assignment_intersection(&self) -> &Term<V> {
match self {
PackageAssignments::Decision((_, term)) => term,
PackageAssignments::Derivations {
intersected,
not_intersected_yet,
} => {
for derivation in not_intersected_yet.iter() {
*intersected = intersected.intersection(&derivation);
}
not_intersected_yet.clear();
intersected
}
PackageAssignments::Derivations(term) => term,
}
}

Expand All @@ -171,17 +147,13 @@ impl<V: Version> PackageAssignments<V> {
/// and if it contains at least one positive derivation term
/// in the partial solution.
fn potential_package_filter<'a, P: Package>(
&'a mut self,
&'a self,
package: &'a P,
) -> Option<(&'a P, &'a Range<V>)> {
match self {
PackageAssignments::Decision(_) => None,
PackageAssignments::Derivations {
intersected,
not_intersected_yet,
} => {
if intersected.is_positive() || not_intersected_yet.iter().any(|t| t.is_positive())
{
PackageAssignments::Derivations(intersected) => {
if intersected.is_positive() {
Some((package, self.assignment_intersection().unwrap_positive()))
} else {
None
Expand Down
13 changes: 7 additions & 6 deletions src/internal/partial_solution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
}

/// Compute, cache and retrieve the intersection of all terms for this package.
pub fn term_intersection_for_package(&mut self, package: &P) -> Option<&Term<V>> {
pub fn term_intersection_for_package(&self, package: &P) -> Option<&Term<V>> {
self.memory.term_intersection_for_package(package)
}

Expand Down Expand Up @@ -130,7 +130,7 @@ impl<P: Package, V: Version> PartialSolution<P, V> {

/// Extract potential packages for the next iteration of unit propagation.
/// Return `None` if there is no suitable package anymore, which stops the algorithm.
pub fn potential_packages(&mut self) -> Option<impl Iterator<Item = (&P, &Range<V>)>> {
pub fn potential_packages(&self) -> Option<impl Iterator<Item = (&P, &Range<V>)>> {
let mut iter = self.memory.potential_packages().peekable();
if iter.peek().is_some() {
Some(iter)
Expand All @@ -151,12 +151,13 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
new_incompatibilities: std::ops::Range<IncompId<P, V>>,
store: &Arena<Incompatibility<P, V>>,
) {
let exact = &Term::exact(version.clone());
let not_satisfied = |incompat: &Incompatibility<P, V>| {
incompat.relation(|p| {
if p == &package {
Some(Term::exact(version.clone()))
Some(exact)
} else {
self.memory.term_intersection_for_package(p).cloned()
self.memory.term_intersection_for_package(p)
}
}) != Relation::Satisfied
};
Expand All @@ -169,8 +170,8 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
}

/// Check if the terms in the partial solution satisfy the incompatibility.
pub fn relation(&mut self, incompat: &Incompatibility<P, V>) -> Relation<P> {
incompat.relation(|package| self.memory.term_intersection_for_package(package).cloned())
pub fn relation(&self, incompat: &Incompatibility<P, V>) -> Relation<P> {
incompat.relation(|package| self.memory.term_intersection_for_package(package))
}

/// Find satisfier and previous satisfier decision level.
Expand Down

0 comments on commit 9b2518e

Please sign in to comment.