[zypp-devel] Re: [zypp-commit] r10079 - /trunk/sat-solver/doc/SOLVER
I know someone who has told us to document all libzypp stuff in Wiki ;-) http://en.opensuse.org/Package_Management/Sat_Solver/Internals Greetings Stefan kkaempf@svn.opensuse.org schrieb:
Author: kkaempf Date: Tue May 13 18:10:56 2008 New Revision: 10079
URL: http://svn.opensuse.org/viewcvs/zypp?rev=10079&view=rev Log: In the beginning, there was nothing. On the first weekend, an initial documentation was created.
Added: trunk/sat-solver/doc/SOLVER
Added: trunk/sat-solver/doc/SOLVER URL: http://svn.opensuse.org/viewcvs/zypp/trunk/sat-solver/doc/SOLVER?rev=10079&view=auto ============================================================================== --- trunk/sat-solver/doc/SOLVER (added) +++ trunk/sat-solver/doc/SOLVER Tue May 13 18:10:56 2008 @@ -0,0 +1,209 @@ +How the sat-solver works +------------------------ + +I. Data structure +----------------- + Id p; /* first literal in rule */ + Id d; /* Id offset into 'list of providers */ + /* terminated by 0' as used by whatprovides; pool->whatprovides + d */ + /* in case of binary rules, d == 0, w1 == p, w2 == other literal */ + /* in case of disabled rules: ~d, aka -d - 1 */ + Id w1, w2; /* watches, literals not-yet-decided */ + /* if !w2, assertion, not rule */ + Id n1, n2; /* next rules in linked list, corresponding to w1,w2 */ + + + r->n1 = solv->watches[nsolvables + r->w1]; + solv->watches[nsolvables + r->w1] = r - solv->rules; + + r->n2 = solv->watches[nsolvables + r->w2]; + solv->watches[nsolvables + r->w2] = r - solv->rules; + + + * Assertion + * keepinstalled (A), install + p=A, d=0, w1=p, w2=0 + * uninstallable (-A), remove + p=-A, d=0, w1=p, w2=0 + * + * Binary rules: + * (A|B) + p=A, d=0, w1=p, w2=B + * (-A|B) + p=-A, d=0, w1=p, w2=B + * + * A requires B : !A | provider1(B) | provider2(B) + p=-A, d=
, w1=, w2= + + * B updates A : A | provider1(B) | provider2(B) + p=A, d= , w1=, w2= + + * + * A conflicts B : (!A | !provider1(B)) & (!A | !provider2(B)) ... + p=-A, d=-B1, w1=, w2= + p=-A, d=-B2, w1=, w2= + p=-A, d=-B3, w1=, w2= + ... + * + * 'not' is encoded as a negative Id + * + +Action | p | d | w1 | w2 +--------------+----+---------+-------+-------- +Assert A | A | 0 | A | 0 +Assert -A |-A | 0 |-A | 0 +Binary A,B | A | 0 | A | B +Binary -A,B |-A | 0 |-A | B +A requires B |-A | prov(B) |-A | whatprovidesdata(B) +B updates A | A | prov(B) | A | whatprovidesdata(B) +A conflicts B |-A | -B |-A |-B + + +addrule(p, d) + + +/* +* add rule +* p = direct literal; always < 0 for installed rpm rules +* d, if < 0 direct literal, if > 0 offset into whatprovides, if == 0 rule is assertion (look at p only) +* +* +* A requires b, b provided by B1,B2,B3 => (-A|B1|B2|B3) +* +* p < 0 : pkg id of A +* d > 0 : Offset in whatprovidesdata (list of providers of b) +* +* A conflicts b, b provided by B1,B2,B3 => (-A|-B1), (-A|-B2), (-A|-B3) +* p < 0 : pkg id of A +* d < 0 : Id of solvable (e.g. B1) +* +* d == 0: unary rule, assertion => (A) or (-A) +* +* Install: p > 0, d = 0 (A) user requested install +* Remove: p < 0, d = 0 (-A) user requested remove +* Requires: p < 0, d > 0 (-A|B1|B2|...) d: <list of providers for requirement of p> +* Updates: p > 0, d > 0 (A|B1|B2|...) d: <list of updates for solvable p> +* Conflicts: p < 0, d < 0 (-A|-B1),(-A|-B2),... either p (conflict issuer) or d (conflict provider) (binary rule) +* Obsoletes: p < 0, d < 0 (-A|-B1),(-A|-B2),... A: uninstalled, Bx = provider of obsoletes +* ? p > 0, d < 0 (A|-B) +* No-op: p = 0, d = 0 (null) (used as policy rule placeholder) +* install one of: p =-SYS, d > 0 +* +* resulting watches: +* ------------------ +* Direct assertion (no watch needed)( if d <0 ) --> d = 0, w1 = p, w2 = 0 +* Binary rule: p = first literal, d = 0, w2 = second literal, w1 = p +* every other : w1 = p, w2 = whatprovidesdata[d]; +* Disabled rule: d < 0, w1 = 0 +* +* always returns a rule for non-rpm rules +*/ + +enablerule: + if (d < 0): d = -d - 1 +disablerule + if (d >= 0): d = -d - 1 + + +Watches +------- + +Watches 'link' rules involving the same literal +Only for n-ary (n >= 2) rules, not for assertions + + +Watches start at solv->watches[nsolvables] + +watches[nsolvables+s] == rule installing s +watches[nsolvables-s] == rule removing s + +'watches trigger when literal goes false' + +watches[A] = rule involving A + rule->n1 = next rule involving A +watches[B] = rule involving B + rule->n2 = next rule involving B + + +Propagation +----------- + +This distributes decisions among affected rules and checks that rules +evaluate to 'true'. + +Since rules are in CNF (conjunctive normal form), it is sufficient for +one sub-term (x or y in x|y) to become 'true'. + +The interesting case is a 'false' sub-term (x or y in x|y) because +this requires the 'other' side to become true. + + + +II. Algorithm +------------- + +- set up rules for installed packages +if (installed) + foreach(installed): addrpmrulesforsolvable + convert dependencies of solvable into rules + run through the complete graph of dependencies spawned the solvable + foreach(installed): addrpmrulesforupdaters + add rules to allow updates: A | upates(A) + +- set up rules for solvables mentioned in job + foreach (install request) + addrpmrulesforsolvable + foreach (update request) + addrpmrulesforupdaters + /* dont look at removals */ + +- addrpmrulesforweak + if suggests: add dependency rule + else if freshens: add dependency rule + else if enhances: add dependency rule + +- add feature rules + +- add update rules + +- add job rules + +During solving, we only look at those rules affected by the job or a +decision. By use of the linked-list rooted at 'watches', the solver is +able to wade through all the rules and pick the right ones. + +And only those have to evaluate to 'true' during propagation. + +Actual solving works as follows +------------------------------- + +- ensure the SYSTEM solvable gets installed +- look through the job rules for 'unit' rules + (i.e. rules directly installing/removing a solvable) + +- These two define the 'initial decisions' which now get propagated +- propagation either results in other decisions (i.e. one sub-term + false requires the other one to become true) or 'free' literals + (which are decided by policy heuristics) + +- The solver has a 'decisionq' which record decisions (> 0 for + 'install', < 0 for 'remove') + Every decision has an 'index' (== position in decisionq) + There are two 'pointers' + - decisionq.count == total number of made decision + - propagate_index == number of already propagated decisions + + Decisions are also recorded in decisionmap, indexed by the solvable + Id. This is a fast access to decisions (instead of searching in the + queue) + + And there is 'decisionq_why', indexed by the decision number (level) + returning the rule leading to the decision. + + propagate() now tries to advance propagate_index to decisionq.count + in order to propagate all decisions to all (affected) rules. + propagate() also ensures that decisions are consistent (else its a + solver conflict) and all affected rules evaluate to 'true'. + + When rules are reduced to 'unit' (only one undecided literal, all + others 'false'), the last literal must be decided to 'true'.
-- ******************************************************************************* Stefan Schubert SUSE LINUX GmbH - Maxfeldstrasse 5 - D-90409 Nuernberg, Germany e-mail: schubi@suse.de ------------------------------------------------------------------------------- SUSE LINUX Products GmbH, GF: Markus Rex, HRB 16746 (AG Nürnberg) -- To unsubscribe, e-mail: zypp-devel+unsubscribe@opensuse.org For additional commands, e-mail: zypp-devel+help@opensuse.org
* Stefan Schubert
I know someone who has told us to document all libzypp stuff in Wiki ;-)
http://en.opensuse.org/Package_Management/Sat_Solver/Internals
Absolutely. Especially since some concepts are better explained with graphics. And you might have seen that I already started to re-arrange sat-solver related Wiki pages ;-P doc/SOLVER in its current state is *very* preliminary and currently mostly a 'pool for documentation pieces'. Please bear with me (and mls, who's currently on vacation) until we have a more complete documentation. Klaus -- To unsubscribe, e-mail: zypp-devel+unsubscribe@opensuse.org For additional commands, e-mail: zypp-devel+help@opensuse.org
participants (2)
-
Klaus Kaempf
-
Stefan Schubert