Document the RuleWatchGraph
parent
265533d390
commit
76d3950992
|
@ -13,6 +13,13 @@
|
||||||
namespace Composer\DependencyResolver;
|
namespace Composer\DependencyResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The RuleWatchGraph efficiently propagates decisions to other rules
|
||||||
|
*
|
||||||
|
* All rules generated for solving a SAT problem should be inserted into the
|
||||||
|
* graph. When a decision on a literal is made, the graph can be used to
|
||||||
|
* propagate the decision to all other rules involving the rule, leading to
|
||||||
|
* other trivial decisions resulting from unit clauses.
|
||||||
|
*
|
||||||
* @author Nils Adermann <naderman@naderman.de>
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
*/
|
*/
|
||||||
class RuleWatchGraph
|
class RuleWatchGraph
|
||||||
|
@ -20,8 +27,16 @@ class RuleWatchGraph
|
||||||
protected $watchChains = array();
|
protected $watchChains = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alters watch chains for a rule.
|
* Inserts a rule node into the appropriate chains within the graph
|
||||||
*
|
*
|
||||||
|
* The node is prepended to the watch chains for each of the two literals it
|
||||||
|
* watches.
|
||||||
|
*
|
||||||
|
* Assertions are skipped because they only depend on a single package and
|
||||||
|
* have no alternative literal that could be true, so there is no need to
|
||||||
|
* watch chnages in any literals.
|
||||||
|
*
|
||||||
|
* @param RuleWatchNode $node The rule node to be inserted into the graph
|
||||||
*/
|
*/
|
||||||
public function insert(RuleWatchNode $node)
|
public function insert(RuleWatchNode $node)
|
||||||
{
|
{
|
||||||
|
@ -38,13 +53,41 @@ class RuleWatchGraph
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function contains($literal)
|
/**
|
||||||
|
* Propagates a decision on a literal to all rules watching the literal
|
||||||
|
*
|
||||||
|
* If a decision, e.g. +A has been made, then all rules containing -A, e.g.
|
||||||
|
* (-A|+B|+C) now need to satisfy at least one of the other literals, so
|
||||||
|
* that the rule as a whole becomes true, since with +A applied the rule
|
||||||
|
* is now (false|+B|+C) so essentialy (+B|+C).
|
||||||
|
*
|
||||||
|
* This means that all rules watching the literal -A need to be updated to
|
||||||
|
* watch 2 other literals which can still be satisfied instead. So literals
|
||||||
|
* that conflict with previously made decisions are not an option.
|
||||||
|
*
|
||||||
|
* Alternatively it can occur that a unit clause results: e.g. if in the
|
||||||
|
* above example the rule was (-A|+B), then A turning true means that
|
||||||
|
* B must now be decided true as well.
|
||||||
|
*
|
||||||
|
* @param int $decidedLiteral The literal which was decided (A in our example)
|
||||||
|
* @param int $level The level at which the decision took place and at which
|
||||||
|
* all resulting decisions should be made.
|
||||||
|
* @param Callable $decisionsSatisfyCallback A callback which checks if a
|
||||||
|
* literal has already been positively decided and the rule is thus
|
||||||
|
* already true and can be skipped.
|
||||||
|
* @param Callable $conflictCallback A callback which checks if a literal
|
||||||
|
* would conflict with previously made decisions on the same package
|
||||||
|
* @param Callable $decideCallback A callback which is responsible for
|
||||||
|
* registering decided literals resulting from unit clauses
|
||||||
|
* @return Rule|null If a conflict is found the conflicting rule is returned
|
||||||
|
*/
|
||||||
|
public function propagateLiteral($decidedLiteral, $level, $decisionsSatisfyCallback, $conflictCallback, $decideCallback)
|
||||||
{
|
{
|
||||||
return isset($this->watchChains[$literal]);
|
// we invert the decided literal here, example:
|
||||||
}
|
// A was decided => (-A|B) now requires B to be true, so we look for
|
||||||
|
// rules which are fulfilled by -A, rather than A.
|
||||||
|
$literal = -$decidedLiteral;
|
||||||
|
|
||||||
public function propagateLiteral($literal, $level, $skipCallback, $conflictCallback, $decideCallback)
|
|
||||||
{
|
|
||||||
if (!isset($this->watchChains[$literal])) {
|
if (!isset($this->watchChains[$literal])) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +99,7 @@ class RuleWatchGraph
|
||||||
$node = $chain->current();
|
$node = $chain->current();
|
||||||
$otherWatch = $node->getOtherWatch($literal);
|
$otherWatch = $node->getOtherWatch($literal);
|
||||||
|
|
||||||
if (!$node->getRule()->isDisabled() && !call_user_func($skipCallback, $otherWatch)) {
|
if (!$node->getRule()->isDisabled() && !call_user_func($decisionsSatisfyCallback, $otherWatch)) {
|
||||||
$ruleLiterals = $node->getRule()->getLiterals();
|
$ruleLiterals = $node->getRule()->getLiterals();
|
||||||
|
|
||||||
$alternativeLiterals = array_filter($ruleLiterals, function ($ruleLiteral) use ($literal, $otherWatch, $conflictCallback) {
|
$alternativeLiterals = array_filter($ruleLiterals, function ($ruleLiteral) use ($literal, $otherWatch, $conflictCallback) {
|
||||||
|
@ -84,6 +127,15 @@ class RuleWatchGraph
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves a rule node from one watch chain to another
|
||||||
|
*
|
||||||
|
* The rule node's watched literals are updated accordingly.
|
||||||
|
*
|
||||||
|
* @param $fromLiteral A literal the node used to watch
|
||||||
|
* @param $toLiteral A literal the node should watch now
|
||||||
|
* @param $node The rule node to be moved
|
||||||
|
*/
|
||||||
protected function moveWatch($fromLiteral, $toLiteral, $node)
|
protected function moveWatch($fromLiteral, $toLiteral, $node)
|
||||||
{
|
{
|
||||||
if (!isset($this->watchChains[$toLiteral])) {
|
if (!isset($this->watchChains[$toLiteral])) {
|
||||||
|
|
|
@ -289,16 +289,8 @@ class Solver
|
||||||
protected function propagate($level)
|
protected function propagate($level)
|
||||||
{
|
{
|
||||||
while ($this->propagateIndex < count($this->decisionQueue)) {
|
while ($this->propagateIndex < count($this->decisionQueue)) {
|
||||||
// we invert the decided literal here, example:
|
|
||||||
// A was decided => (-A|B) now requires B to be true, so we look for
|
|
||||||
// rules which are fulfilled by -A, rather than A.
|
|
||||||
|
|
||||||
$literal = -$this->decisionQueue[$this->propagateIndex];
|
|
||||||
|
|
||||||
$this->propagateIndex++;
|
|
||||||
|
|
||||||
$conflict = $this->watchGraph->propagateLiteral(
|
$conflict = $this->watchGraph->propagateLiteral(
|
||||||
$literal,
|
$this->decisionQueue[$this->propagateIndex++],
|
||||||
$level,
|
$level,
|
||||||
array($this, 'decisionsContain'),
|
array($this, 'decisionsContain'),
|
||||||
array($this, 'decisionsConflict'),
|
array($this, 'decisionsConflict'),
|
||||||
|
|
Loading…
Reference in New Issue