@@ -30,36 +30,61 @@ impl DecisionLevel {
3030#[ derive( Clone , Debug ) ]
3131pub ( crate ) struct PartialSolution < DP : DependencyProvider > {
3232 next_global_index : u32 ,
33+ /// The number of decisions that have been made, equal to the number of packages with decisions.
3334 current_decision_level : DecisionLevel ,
34- /// `package_assignments` is primarily a HashMap from a package to its
35- /// `PackageAssignments`. But it can also keep the items in an order.
36- /// We maintain three sections in this order:
37- /// 1. `[..current_decision_level]` Are packages that have had a decision made sorted by the `decision_level`.
38- /// This makes it very efficient to extract the solution, And to backtrack to a particular decision level.
39- /// 2. `[current_decision_level..changed_this_decision_level]` Are packages that have **not** had there assignments
40- /// changed since the last time `prioritize` has been called. Within this range there is no sorting.
41- /// 3. `[changed_this_decision_level..]` Contains all packages that **have** had there assignments changed since
42- /// the last time `prioritize` has been called. The inverse is not necessarily true, some packages in the range
43- /// did not have a change. Within this range there is no sorting.
35+ /// Store for all known package decisions and package derivations.
36+ ///
37+ /// "assignment" refers to both packages with decisions and package with only derivations and
38+ /// no decision yet. We combine this in a single index map, where different sections (of
39+ /// indexes) contain package with different level of information, and make a decision moves a
40+ /// package from the derivations sections to the decisions section.
41+ ///
42+ /// `[..current_decision_level]`: Packages that have had a decision made, sorted by the
43+ /// `decision_level`. The section is can be seen as the partial solution, it contains a
44+ /// mapping from package name to decided version. The sorting makes it very efficient to
45+ /// extract the solution, and to backtrack to a particular decision level. The
46+ /// `AssignmentsIntersection` is always a `Decision`.
47+ ///
48+ /// `[prioritize_decision_level..]`: Packages that are dependencies of some other package,
49+ /// but have not yet been decided. The `AssignmentsIntersection` is always a `Derivations`, the
50+ /// derivations store the obligations from the decided packages. This section has two
51+ /// subsections to optimize the number of `prioritize` calls:
52+ ///
53+ /// `[current_decision_level..prioritize_decision_level]`: The assignments of packages in this
54+ /// range have not changed since the last time `prioritize` has been called, their
55+ /// priority in `prioritized_potential_packages` is fresh. There is no sorting within this
56+ /// range.
57+ ///
58+ /// `[prioritize_decision_level..]`: The assignments of packages in this range may have changed
59+ /// since the last time `prioritize` has been called, their priority in
60+ /// `prioritized_potential_packages` needs to be refreshed. There is no sorting within this
61+ /// range.
4462 #[ allow( clippy:: type_complexity) ]
4563 package_assignments : FnvIndexMap < Id < DP :: P > , PackageAssignments < DP :: P , DP :: VS , DP :: M > > ,
46- /// `prioritized_potential_packages` is primarily a HashMap from a package with no desition and a positive assignment
47- /// to its `Priority`. But, it also maintains a max heap of packages by `Priority` order.
64+ /// Index into `package_assignments` to decide which packages need to be re-prioritized.
65+ prioritize_decision_level : usize ,
66+ /// The undecided packages order by their `Priority`.
67+ ///
68+ /// The max heap allows quickly `pop`ing the highest priority package.
4869 prioritized_potential_packages :
4970 PriorityQueue < Id < DP :: P > , DP :: Priority , BuildHasherDefault < FxHasher > > ,
50- changed_this_decision_level : usize ,
71+ /// Whether we have never backtracked, to enable fast path optimizations.
5172 has_ever_backtracked : bool ,
5273}
5374
54- /// Package assignments contain the potential decision and derivations
55- /// that have already been made for a given package,
56- /// as well as the intersection of terms by all of these.
75+ /// A package assignment is either a decision or a list of (accumulated) derivations without a
76+ /// decision.
5777#[ derive( Clone , Debug ) ]
5878struct PackageAssignments < P : Package , VS : VersionSet , M : Eq + Clone + Debug + Display > {
79+ /// Whether the assigment is a decision or a derivation.
80+ assignments_intersection : AssignmentsIntersection < VS > ,
81+ /// All constraints on the package version from previous decisions, accumulated by decision
82+ /// level.
83+ dated_derivations : SmallVec < DatedDerivation < P , VS , M > > ,
84+ /// Smallest [`DecisionLevel`] in `dated_derivations`.
5985 smallest_decision_level : DecisionLevel ,
86+ /// Highest [`DecisionLevel`] in `dated_derivations`.
6087 highest_decision_level : DecisionLevel ,
61- dated_derivations : SmallVec < DatedDerivation < P , VS , M > > ,
62- assignments_intersection : AssignmentsIntersection < VS > ,
6388}
6489
6590impl < P : Package , VS : VersionSet , M : Eq + Clone + Debug + Display > Display
@@ -85,8 +110,13 @@ impl<P: Package, VS: VersionSet, M: Eq + Clone + Debug + Display> Display
85110#[ derive( Clone , Debug ) ]
86111struct DatedDerivation < P : Package , VS : VersionSet , M : Eq + Clone + Debug + Display > {
87112 global_index : u32 ,
113+ /// Only decisions up this level has been used to compute the accumulated term.
88114 decision_level : DecisionLevel ,
89115 cause : IncompId < P , VS , M > ,
116+ /// The intersection of all terms up to `decision_level`.
117+ ///
118+ /// It may not contain all terms of this `decision_level`, there may be more than one
119+ /// `DatedDerivation` per decision level.
90120 accumulated_intersection : Term < VS > ,
91121}
92122
@@ -100,15 +130,25 @@ impl<P: Package, VS: VersionSet, M: Eq + Clone + Debug + Display> Display
100130
101131#[ derive( Clone , Debug ) ]
102132enum AssignmentsIntersection < VS : VersionSet > {
103- Decision ( ( u32 , VS :: V , Term < VS > ) ) ,
133+ /// A decision on package for version has been made at the given level.
134+ Decision {
135+ decision_level : u32 ,
136+ version : VS :: V ,
137+ /// The version, but as positive singleton term.
138+ term : Term < VS > ,
139+ } ,
104140 Derivations ( Term < VS > ) ,
105141}
106142
107143impl < VS : VersionSet > Display for AssignmentsIntersection < VS > {
108144 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
109145 match self {
110- Self :: Decision ( ( lvl, version, _) ) => {
111- write ! ( f, "Decision: level {}, v = {}" , lvl, version)
146+ Self :: Decision {
147+ decision_level,
148+ version,
149+ term : _,
150+ } => {
151+ write ! ( f, "Decision: level {}, v = {}" , decision_level, version)
112152 }
113153 Self :: Derivations ( term) => write ! ( f, "Derivations term: {}" , term) ,
114154 }
@@ -135,7 +175,7 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
135175 current_decision_level : DecisionLevel ( 0 ) ,
136176 package_assignments : FnvIndexMap :: default ( ) ,
137177 prioritized_potential_packages : PriorityQueue :: default ( ) ,
138- changed_this_decision_level : 0 ,
178+ prioritize_decision_level : 0 ,
139179 has_ever_backtracked : false ,
140180 }
141181 }
@@ -173,7 +213,9 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
173213 None => panic ! ( "Derivations must already exist" ) ,
174214 Some ( pa) => match & pa. assignments_intersection {
175215 // Cannot be called when a decision has already been taken.
176- AssignmentsIntersection :: Decision ( _) => panic ! ( "Already existing decision" ) ,
216+ AssignmentsIntersection :: Decision { .. } => {
217+ panic ! ( "Already existing decision" )
218+ }
177219 // Cannot be called if the versions is not contained in the terms' intersection.
178220 AssignmentsIntersection :: Derivations ( term) => {
179221 debug_assert ! (
@@ -187,7 +229,7 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
187229 } ,
188230 }
189231 assert_eq ! (
190- self . changed_this_decision_level ,
232+ self . prioritize_decision_level ,
191233 self . package_assignments. len( )
192234 ) ;
193235 }
@@ -198,11 +240,11 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
198240 . get_full_mut ( & package)
199241 . expect ( "Derivations must already exist" ) ;
200242 pa. highest_decision_level = self . current_decision_level ;
201- pa. assignments_intersection = AssignmentsIntersection :: Decision ( (
202- self . next_global_index ,
203- version. clone ( ) ,
204- Term :: exact ( version) ,
205- ) ) ;
243+ pa. assignments_intersection = AssignmentsIntersection :: Decision {
244+ decision_level : self . next_global_index ,
245+ version : version . clone ( ) ,
246+ term : Term :: exact ( version) ,
247+ } ;
206248 // Maintain that the beginning of the `package_assignments` Have all decisions in sorted order.
207249 if new_idx != old_idx {
208250 self . package_assignments . swap_indices ( new_idx, old_idx) ;
@@ -233,17 +275,17 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
233275 pa. highest_decision_level = self . current_decision_level ;
234276 match & mut pa. assignments_intersection {
235277 // Check that add_derivation is never called in the wrong context.
236- AssignmentsIntersection :: Decision ( _ ) => {
278+ AssignmentsIntersection :: Decision { .. } => {
237279 panic ! ( "add_derivation should not be called after a decision" )
238280 }
239281 AssignmentsIntersection :: Derivations ( t) => {
240282 * t = t. intersection ( & dated_derivation. accumulated_intersection ) ;
241283 dated_derivation. accumulated_intersection = t. clone ( ) ;
242284 if t. is_positive ( ) {
243- // we can use `swap_indices` to make `changed_this_decision_level ` only go down by 1
285+ // we can use `swap_indices` to make `prioritize_decision_level ` only go down by 1
244286 // but the copying is slower then the larger search
245- self . changed_this_decision_level =
246- std:: cmp:: min ( self . changed_this_decision_level , idx) ;
287+ self . prioritize_decision_level =
288+ std:: cmp:: min ( self . prioritize_decision_level , idx) ;
247289 }
248290 }
249291 }
@@ -252,8 +294,8 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
252294 Entry :: Vacant ( v) => {
253295 let term = dated_derivation. accumulated_intersection . clone ( ) ;
254296 if term. is_positive ( ) {
255- self . changed_this_decision_level =
256- std:: cmp:: min ( self . changed_this_decision_level , pa_last_index) ;
297+ self . prioritize_decision_level =
298+ std:: cmp:: min ( self . prioritize_decision_level , pa_last_index) ;
257299 }
258300 v. insert ( PackageAssignments {
259301 smallest_decision_level : self . current_decision_level ,
@@ -270,12 +312,12 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
270312 & mut self ,
271313 prioritizer : impl Fn ( Id < DP :: P > , & DP :: VS ) -> DP :: Priority ,
272314 ) -> Option < Id < DP :: P > > {
273- let check_all = self . changed_this_decision_level
315+ let check_all = self . prioritize_decision_level
274316 == self . current_decision_level . 0 . saturating_sub ( 1 ) as usize ;
275317 let current_decision_level = self . current_decision_level ;
276318 let prioritized_potential_packages = & mut self . prioritized_potential_packages ;
277319 self . package_assignments
278- . get_range ( self . changed_this_decision_level ..)
320+ . get_range ( self . prioritize_decision_level ..)
279321 . unwrap ( )
280322 . iter ( )
281323 . filter ( |( _, pa) | {
@@ -290,7 +332,7 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
290332 let priority = prioritizer ( p, r) ;
291333 prioritized_potential_packages. push ( p, priority) ;
292334 } ) ;
293- self . changed_this_decision_level = self . package_assignments . len ( ) ;
335+ self . prioritize_decision_level = self . package_assignments . len ( ) ;
294336 prioritized_potential_packages. pop ( ) . map ( |( p, _) | p)
295337 }
296338
@@ -302,7 +344,11 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
302344 . iter ( )
303345 . take ( self . current_decision_level . 0 as usize )
304346 . map ( |( & p, pa) | match & pa. assignments_intersection {
305- AssignmentsIntersection :: Decision ( ( _, v, _) ) => ( p, v. clone ( ) ) ,
347+ AssignmentsIntersection :: Decision {
348+ decision_level : _,
349+ version : v,
350+ term : _,
351+ } => ( p, v. clone ( ) ) ,
306352 AssignmentsIntersection :: Derivations ( _) => {
307353 panic ! ( "Derivations in the Decision part" )
308354 }
@@ -347,7 +393,7 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
347393 } ) ;
348394 // Throw away all stored priority levels, And mark that they all need to be recomputed.
349395 self . prioritized_potential_packages . clear ( ) ;
350- self . changed_this_decision_level = self . current_decision_level . 0 . saturating_sub ( 1 ) as usize ;
396+ self . prioritize_decision_level = self . current_decision_level . 0 . saturating_sub ( 1 ) as usize ;
351397 self . has_ever_backtracked = true ;
352398 }
353399
@@ -484,7 +530,11 @@ impl<DP: DependencyProvider> PartialSolution<DP> {
484530 } else {
485531 match & satisfier_pa. assignments_intersection {
486532 AssignmentsIntersection :: Derivations ( _) => panic ! ( "must be a decision" ) ,
487- AssignmentsIntersection :: Decision ( ( _, _, term) ) => term. clone ( ) ,
533+ AssignmentsIntersection :: Decision {
534+ decision_level : _,
535+ version : _,
536+ term,
537+ } => term. clone ( ) ,
488538 }
489539 } ;
490540
@@ -532,9 +582,11 @@ impl<P: Package, VS: VersionSet, M: Eq + Clone + Debug + Display> PackageAssignm
532582 // If it wasn't found in the derivations,
533583 // it must be the decision which is last (if called in the right context).
534584 match & self . assignments_intersection {
535- AssignmentsIntersection :: Decision ( ( global_index, _, _) ) => {
536- ( None , * global_index, self . highest_decision_level )
537- }
585+ AssignmentsIntersection :: Decision {
586+ decision_level : global_index,
587+ version : _,
588+ term : _,
589+ } => ( None , * global_index, self . highest_decision_level ) ,
538590 AssignmentsIntersection :: Derivations ( accumulated_intersection) => {
539591 unreachable ! (
540592 concat!(
@@ -555,7 +607,11 @@ impl<VS: VersionSet> AssignmentsIntersection<VS> {
555607 /// Returns the term intersection of all assignments (decision included).
556608 fn term ( & self ) -> & Term < VS > {
557609 match self {
558- Self :: Decision ( ( _, _, term) ) => term,
610+ Self :: Decision {
611+ decision_level : _,
612+ version : _,
613+ term,
614+ } => term,
559615 Self :: Derivations ( term) => term,
560616 }
561617 }
@@ -566,7 +622,7 @@ impl<VS: VersionSet> AssignmentsIntersection<VS> {
566622 /// in the partial solution.
567623 fn potential_package_filter < P : Package > ( & self , package : Id < P > ) -> Option < ( Id < P > , & VS ) > {
568624 match self {
569- Self :: Decision ( _ ) => None ,
625+ Self :: Decision { .. } => None ,
570626 Self :: Derivations ( term_intersection) => {
571627 if term_intersection. is_positive ( ) {
572628 Some ( ( package, term_intersection. unwrap_positive ( ) ) )
0 commit comments