2011-04-05 15:37:19 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This file is part of Composer.
|
|
|
|
*
|
|
|
|
* (c) Nils Adermann <naderman@naderman.de>
|
2011-04-16 12:42:35 +00:00
|
|
|
* Jordi Boggiano <j.boggiano@seld.be>
|
2011-04-05 15:37:19 +00:00
|
|
|
*
|
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Composer\DependencyResolver;
|
|
|
|
|
2011-05-22 07:04:09 +00:00
|
|
|
use Composer\Repository\RepositoryInterface;
|
2012-04-27 17:41:53 +00:00
|
|
|
use Composer\Package\AliasPackage;
|
2011-09-23 23:29:22 +00:00
|
|
|
use Composer\DependencyResolver\Operation;
|
2011-05-22 07:04:09 +00:00
|
|
|
|
2011-04-05 15:37:19 +00:00
|
|
|
/**
|
|
|
|
* @author Nils Adermann <naderman@naderman.de>
|
|
|
|
*/
|
|
|
|
class Solver
|
|
|
|
{
|
|
|
|
protected $policy;
|
|
|
|
protected $pool;
|
|
|
|
protected $installed;
|
|
|
|
protected $rules;
|
2012-05-15 19:36:47 +00:00
|
|
|
protected $ruleSetGenerator;
|
2011-04-05 15:37:19 +00:00
|
|
|
protected $updateAll;
|
|
|
|
|
|
|
|
protected $addedMap = array();
|
|
|
|
protected $updateMap = array();
|
2012-05-18 23:27:57 +00:00
|
|
|
protected $watchGraph;
|
2011-08-21 11:30:10 +00:00
|
|
|
protected $decisionMap;
|
2011-11-18 23:27:35 +00:00
|
|
|
protected $installedMap;
|
2011-04-05 15:37:19 +00:00
|
|
|
|
2012-04-27 16:22:55 +00:00
|
|
|
protected $decisionQueue = array();
|
|
|
|
protected $decisionQueueWhy = array();
|
|
|
|
protected $decisionQueueFree = array();
|
|
|
|
protected $propagateIndex;
|
|
|
|
protected $branches = array();
|
|
|
|
protected $problems = array();
|
|
|
|
protected $learnedPool = array();
|
|
|
|
|
2012-01-17 21:39:36 +00:00
|
|
|
public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed)
|
2011-04-05 15:37:19 +00:00
|
|
|
{
|
|
|
|
$this->policy = $policy;
|
|
|
|
$this->pool = $pool;
|
|
|
|
$this->installed = $installed;
|
2012-05-15 19:36:47 +00:00
|
|
|
$this->ruleSetGenerator = new RuleSetGenerator($policy, $pool);
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
private function findDecisionRule($packageId)
|
2011-04-05 15:37:19 +00:00
|
|
|
{
|
|
|
|
foreach ($this->decisionQueue as $i => $literal) {
|
2012-05-19 18:38:56 +00:00
|
|
|
if ($packageId === abs($literal)) {
|
2011-04-05 15:37:19 +00:00
|
|
|
return $this->decisionQueueWhy[$i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2011-07-08 11:09:39 +00:00
|
|
|
// aka solver_makeruledecisions
|
2011-04-05 15:37:19 +00:00
|
|
|
private function makeAssertionRuleDecisions()
|
|
|
|
{
|
2011-07-08 11:09:39 +00:00
|
|
|
$decisionStart = count($this->decisionQueue);
|
|
|
|
|
|
|
|
for ($ruleIndex = 0; $ruleIndex < count($this->rules); $ruleIndex++) {
|
|
|
|
$rule = $this->rules->ruleById($ruleIndex);
|
|
|
|
|
2012-05-19 19:49:48 +00:00
|
|
|
if (!$rule->isAssertion() || $rule->isDisabled()) {
|
2011-04-05 15:37:19 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-06-26 22:11:57 +00:00
|
|
|
$literals = $rule->getLiterals();
|
|
|
|
$literal = $literals[0];
|
2011-04-05 15:37:19 +00:00
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
if (!$this->decided(abs($literal))) {
|
|
|
|
$this->decide($literal, 1, $rule);
|
2011-06-26 22:11:57 +00:00
|
|
|
continue;
|
|
|
|
}
|
2011-04-05 15:37:19 +00:00
|
|
|
|
2011-06-26 22:11:57 +00:00
|
|
|
if ($this->decisionsSatisfy($literal)) {
|
|
|
|
continue;
|
|
|
|
}
|
2011-04-05 15:37:19 +00:00
|
|
|
|
2011-06-26 22:11:57 +00:00
|
|
|
// found a conflict
|
|
|
|
if (RuleSet::TYPE_LEARNED === $rule->getType()) {
|
|
|
|
$rule->disable();
|
2011-07-08 11:09:39 +00:00
|
|
|
continue;
|
2011-06-26 22:11:57 +00:00
|
|
|
}
|
2011-04-05 15:37:19 +00:00
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
$conflict = $this->findDecisionRule(abs($literal));
|
2011-07-08 11:09:39 +00:00
|
|
|
|
2011-06-26 22:11:57 +00:00
|
|
|
if ($conflict && RuleSet::TYPE_PACKAGE === $conflict->getType()) {
|
2011-04-05 15:37:19 +00:00
|
|
|
|
2012-03-18 19:41:10 +00:00
|
|
|
$problem = new Problem;
|
|
|
|
|
2012-05-15 19:36:47 +00:00
|
|
|
$problem->addRule($rule);
|
|
|
|
$problem->addRule($conflict);
|
|
|
|
$this->disableProblem($rule);
|
2012-03-18 19:41:10 +00:00
|
|
|
$this->problems[] = $problem;
|
2011-07-08 11:09:39 +00:00
|
|
|
continue;
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
2011-07-08 11:09:39 +00:00
|
|
|
|
2012-05-19 19:49:48 +00:00
|
|
|
// conflict with another job
|
2012-03-18 19:41:10 +00:00
|
|
|
$problem = new Problem;
|
|
|
|
$problem->addRule($rule);
|
|
|
|
$problem->addRule($conflict);
|
2011-07-08 11:09:39 +00:00
|
|
|
|
2012-05-19 19:49:48 +00:00
|
|
|
// push all of our rules (can only be job rules)
|
2011-07-08 11:09:39 +00:00
|
|
|
// asserting this literal on the problem stack
|
2012-04-27 16:28:18 +00:00
|
|
|
foreach ($this->rules->getIteratorFor(RuleSet::TYPE_JOB) as $assertRule) {
|
2012-05-19 19:49:48 +00:00
|
|
|
if ($assertRule->isDisabled() || !$assertRule->isAssertion()) {
|
2011-07-08 11:09:39 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$assertRuleLiterals = $assertRule->getLiterals();
|
|
|
|
$assertRuleLiteral = $assertRuleLiterals[0];
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
if (abs($literal) !== abs($assertRuleLiteral)) {
|
2011-07-08 11:09:39 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-05-15 19:36:47 +00:00
|
|
|
$problem->addRule($assertRule);
|
|
|
|
$this->disableProblem($assertRule);
|
2011-07-08 11:09:39 +00:00
|
|
|
}
|
2012-03-18 19:41:10 +00:00
|
|
|
$this->problems[] = $problem;
|
2011-07-08 11:09:39 +00:00
|
|
|
|
|
|
|
// start over
|
|
|
|
while (count($this->decisionQueue) > $decisionStart) {
|
|
|
|
$decisionLiteral = array_pop($this->decisionQueue);
|
|
|
|
array_pop($this->decisionQueueWhy);
|
|
|
|
unset($this->decisionQueueFree[count($this->decisionQueue)]);
|
2012-05-19 18:38:56 +00:00
|
|
|
$this->decisionMap[abs($decisionLiteral)] = 0;
|
2011-07-08 11:09:39 +00:00
|
|
|
}
|
|
|
|
$ruleIndex = -1;
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
2012-04-27 15:50:53 +00:00
|
|
|
}
|
|
|
|
|
2012-04-27 15:50:53 +00:00
|
|
|
protected function setupInstalledMap()
|
2012-04-27 15:50:53 +00:00
|
|
|
{
|
2012-04-29 20:21:58 +00:00
|
|
|
$this->installedMap = array();
|
2012-04-27 16:24:17 +00:00
|
|
|
foreach ($this->installed->getPackages() as $package) {
|
2012-04-29 20:21:58 +00:00
|
|
|
$this->installedMap[$package->getId()] = $package;
|
|
|
|
}
|
2012-04-27 15:50:53 +00:00
|
|
|
|
2011-04-05 15:37:19 +00:00
|
|
|
foreach ($this->jobs as $job) {
|
2012-05-15 18:19:38 +00:00
|
|
|
switch ($job['cmd']) {
|
|
|
|
case 'update':
|
|
|
|
foreach ($job['packages'] as $package) {
|
2011-11-18 23:27:35 +00:00
|
|
|
if (isset($this->installedMap[$package->getId()])) {
|
2011-06-03 16:22:20 +00:00
|
|
|
$this->updateMap[$package->getId()] = true;
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
2012-05-15 18:19:38 +00:00
|
|
|
}
|
2012-05-21 16:33:21 +00:00
|
|
|
break;
|
2012-02-19 15:59:04 +00:00
|
|
|
|
|
|
|
case 'update-all':
|
2012-04-27 15:50:53 +00:00
|
|
|
foreach ($this->installedMap as $package) {
|
2012-02-19 15:59:04 +00:00
|
|
|
$this->updateMap[$package->getId()] = true;
|
|
|
|
}
|
2012-05-21 16:33:21 +00:00
|
|
|
break;
|
2012-05-15 19:36:47 +00:00
|
|
|
|
2011-04-05 15:37:19 +00:00
|
|
|
case 'install':
|
2012-05-15 19:36:47 +00:00
|
|
|
if (!$job['packages']) {
|
2012-03-18 19:41:10 +00:00
|
|
|
$problem = new Problem();
|
2012-05-19 18:38:56 +00:00
|
|
|
$problem->addRule(new Rule($this->pool, array(), null, null, $job));
|
2012-03-18 19:41:10 +00:00
|
|
|
$this->problems[] = $problem;
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
2012-05-21 16:33:21 +00:00
|
|
|
break;
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
|
|
|
}
|
2012-05-15 18:19:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function solve(Request $request)
|
|
|
|
{
|
|
|
|
$this->jobs = $request->getJobs();
|
|
|
|
|
|
|
|
$this->setupInstalledMap();
|
|
|
|
|
|
|
|
if (version_compare(PHP_VERSION, '5.3.4', '>=')) {
|
|
|
|
$this->decisionMap = new \SplFixedArray($this->pool->getMaxId() + 1);
|
|
|
|
} else {
|
|
|
|
$this->decisionMap = array_fill(0, $this->pool->getMaxId() + 1, 0);
|
|
|
|
}
|
|
|
|
|
2012-05-15 19:36:47 +00:00
|
|
|
$this->rules = $this->ruleSetGenerator->getRulesFor($this->jobs, $this->installedMap);
|
2012-05-18 23:27:57 +00:00
|
|
|
$this->watchGraph = new RuleWatchGraph;
|
2011-04-05 15:37:19 +00:00
|
|
|
|
2011-07-08 11:09:39 +00:00
|
|
|
foreach ($this->rules as $rule) {
|
2012-05-18 23:27:57 +00:00
|
|
|
$this->watchGraph->insert(new RuleWatchNode($rule));
|
2011-07-08 11:09:39 +00:00
|
|
|
}
|
2011-04-05 15:37:19 +00:00
|
|
|
|
|
|
|
/* make decisions based on job/update assertions */
|
|
|
|
$this->makeAssertionRuleDecisions();
|
|
|
|
|
2012-05-15 18:01:51 +00:00
|
|
|
$this->runSat(true);
|
2011-04-05 15:37:19 +00:00
|
|
|
|
2012-02-18 23:15:23 +00:00
|
|
|
if ($this->problems) {
|
2012-03-18 19:41:10 +00:00
|
|
|
throw new SolverProblemsException($this->problems);
|
2012-02-18 23:15:23 +00:00
|
|
|
}
|
|
|
|
|
2012-05-15 17:55:41 +00:00
|
|
|
$transaction = new Transaction($this->policy, $this->pool, $this->installedMap, $this->decisionMap, $this->decisionQueue, $this->decisionQueueWhy);
|
|
|
|
return $transaction->getOperations();
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
|
|
|
|
2011-06-07 20:43:26 +00:00
|
|
|
protected function literalFromId($id)
|
|
|
|
{
|
2011-08-20 16:32:31 +00:00
|
|
|
$package = $this->pool->packageById(abs($id));
|
2011-06-07 20:43:26 +00:00
|
|
|
return new Literal($package, $id > 0);
|
|
|
|
}
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
protected function addDecision($literal, $level)
|
2011-04-05 15:37:19 +00:00
|
|
|
{
|
2012-05-19 18:38:56 +00:00
|
|
|
$packageId = abs($literal);
|
2012-02-18 23:13:21 +00:00
|
|
|
|
2012-05-09 16:43:17 +00:00
|
|
|
$previousDecision = $this->decisionMap[$packageId];
|
|
|
|
if ($previousDecision != 0) {
|
2012-05-19 18:38:56 +00:00
|
|
|
$literalString = $this->pool->literalToString($literal);
|
|
|
|
$package = $this->pool->literalToPackage($literal);
|
2012-05-09 16:43:17 +00:00
|
|
|
throw new SolverBugException(
|
2012-05-19 18:38:56 +00:00
|
|
|
"Trying to decide $literalString on level $level, even though $package was previously decided as ".(int) $previousDecision."."
|
2012-05-09 16:43:17 +00:00
|
|
|
);
|
|
|
|
}
|
2012-02-18 23:13:21 +00:00
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
if ($literal > 0) {
|
2011-06-07 20:43:26 +00:00
|
|
|
$this->decisionMap[$packageId] = $level;
|
|
|
|
} else {
|
|
|
|
$this->decisionMap[$packageId] = -$level;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-18 23:27:57 +00:00
|
|
|
public function decide($literal, $level, $why)
|
|
|
|
{
|
2012-05-19 18:38:56 +00:00
|
|
|
$this->addDecision($literal, $level);
|
|
|
|
$this->decisionQueue[] = $literal;
|
2012-05-18 23:27:57 +00:00
|
|
|
$this->decisionQueueWhy[] = $why;
|
|
|
|
}
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
public function decisionsContain($literal)
|
2011-04-05 15:37:19 +00:00
|
|
|
{
|
2012-05-19 18:38:56 +00:00
|
|
|
$packageId = abs($literal);
|
2011-08-21 11:30:10 +00:00
|
|
|
return (
|
2012-05-19 18:38:56 +00:00
|
|
|
$this->decisionMap[$packageId] > 0 && $literal > 0 ||
|
|
|
|
$this->decisionMap[$packageId] < 0 && $literal < 0
|
2011-08-21 11:30:10 +00:00
|
|
|
);
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
protected function decisionsSatisfy($literal)
|
2011-06-07 20:43:26 +00:00
|
|
|
{
|
2012-05-19 18:38:56 +00:00
|
|
|
$packageId = abs($literal);
|
2011-08-21 11:30:10 +00:00
|
|
|
return (
|
2012-05-19 18:38:56 +00:00
|
|
|
$literal > 0 && $this->decisionMap[$packageId] > 0 ||
|
|
|
|
$literal < 0 && $this->decisionMap[$packageId] < 0
|
2011-08-21 11:30:10 +00:00
|
|
|
);
|
2011-06-07 20:43:26 +00:00
|
|
|
}
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
public function decisionsConflict($literal)
|
2011-04-05 15:37:19 +00:00
|
|
|
{
|
2012-05-19 18:38:56 +00:00
|
|
|
$packageId = abs($literal);
|
2011-08-21 11:30:10 +00:00
|
|
|
return (
|
2012-05-19 18:38:56 +00:00
|
|
|
($this->decisionMap[$packageId] > 0 && $literal < 0) ||
|
|
|
|
($this->decisionMap[$packageId] < 0 && $literal > 0)
|
2011-08-21 11:30:10 +00:00
|
|
|
);
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
2012-05-19 18:38:56 +00:00
|
|
|
protected function decided($packageId)
|
2011-06-07 20:43:26 +00:00
|
|
|
{
|
2012-05-19 18:38:56 +00:00
|
|
|
return $this->decisionMap[$packageId] != 0;
|
2011-06-07 20:43:26 +00:00
|
|
|
}
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
protected function undecided($packageId)
|
2011-04-05 15:37:19 +00:00
|
|
|
{
|
2012-05-19 18:38:56 +00:00
|
|
|
return $this->decisionMap[$packageId] == 0;
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
protected function decidedInstall($packageId) {
|
|
|
|
return $this->decisionMap[$packageId] > 0;
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Makes a decision and propagates it to all rules.
|
|
|
|
*
|
|
|
|
* Evaluates each term affected by the decision (linked through watches)
|
|
|
|
* If we find unit rules we make new decisions based on them
|
|
|
|
*
|
|
|
|
* @return Rule|null A rule on conflict, otherwise null.
|
|
|
|
*/
|
|
|
|
protected function propagate($level)
|
|
|
|
{
|
|
|
|
while ($this->propagateIndex < count($this->decisionQueue)) {
|
2012-05-19 18:50:21 +00:00
|
|
|
$conflict = $this->watchGraph->propagateLiteral(
|
2012-05-21 16:14:38 +00:00
|
|
|
$this->decisionQueue[$this->propagateIndex++],
|
2012-05-18 23:27:57 +00:00
|
|
|
$level,
|
2012-05-19 18:38:56 +00:00
|
|
|
array($this, 'decisionsContain'),
|
|
|
|
array($this, 'decisionsConflict'),
|
2012-05-18 23:27:57 +00:00
|
|
|
array($this, 'decide')
|
|
|
|
);
|
2011-04-05 15:37:19 +00:00
|
|
|
|
2012-05-18 23:27:57 +00:00
|
|
|
if ($conflict) {
|
|
|
|
return $conflict;
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2011-08-20 17:36:18 +00:00
|
|
|
/**
|
|
|
|
* Reverts a decision at the given level.
|
|
|
|
*/
|
|
|
|
private function revert($level)
|
|
|
|
{
|
|
|
|
while (!empty($this->decisionQueue)) {
|
|
|
|
$literal = $this->decisionQueue[count($this->decisionQueue) - 1];
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
if (!$this->decisionMap[abs($literal)]) {
|
2011-08-20 17:36:18 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
$decisionLevel = abs($this->decisionMap[abs($literal)]);
|
2011-08-20 17:36:18 +00:00
|
|
|
|
|
|
|
if ($decisionLevel <= $level) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
$this->decisionMap[abs($literal)] = 0;
|
2011-08-20 17:36:18 +00:00
|
|
|
array_pop($this->decisionQueue);
|
|
|
|
array_pop($this->decisionQueueWhy);
|
|
|
|
|
|
|
|
$this->propagateIndex = count($this->decisionQueue);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!empty($this->branches)) {
|
|
|
|
list($literals, $branchLevel) = $this->branches[count($this->branches) - 1];
|
|
|
|
|
2011-08-21 03:03:52 +00:00
|
|
|
if ($branchLevel >= $level) {
|
|
|
|
break;
|
2011-08-20 17:36:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
array_pop($this->branches);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-08 11:09:39 +00:00
|
|
|
/**-------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* setpropagatelearn
|
|
|
|
*
|
|
|
|
* add free decision (solvable to install) to decisionq
|
|
|
|
* increase level and propagate decision
|
|
|
|
* return if no conflict.
|
|
|
|
*
|
|
|
|
* in conflict case, analyze conflict rule, add resulting
|
|
|
|
* rule to learnt rule set, make decision from learnt
|
|
|
|
* rule (always unit) and re-propagate.
|
|
|
|
*
|
|
|
|
* returns the new solver level or 0 if unsolvable
|
|
|
|
*
|
|
|
|
*/
|
2012-05-19 18:38:56 +00:00
|
|
|
private function setPropagateLearn($level, $literal, $disableRules, Rule $rule)
|
2011-04-05 15:37:19 +00:00
|
|
|
{
|
2011-07-08 11:09:39 +00:00
|
|
|
$level++;
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
$this->decide($literal, $level, $rule);
|
2011-07-08 11:09:39 +00:00
|
|
|
$this->decisionQueueFree[count($this->decisionQueueWhy) - 1] = true;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
$rule = $this->propagate($level);
|
|
|
|
|
|
|
|
if (!$rule) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($level == 1) {
|
2011-08-20 17:36:18 +00:00
|
|
|
return $this->analyzeUnsolvable($rule, $disableRules);
|
2011-07-08 11:09:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// conflict
|
2012-04-01 20:26:10 +00:00
|
|
|
list($learnLiteral, $newLevel, $newRule, $why) = $this->analyze($level, $rule);
|
2011-07-08 11:09:39 +00:00
|
|
|
|
2012-05-09 16:43:17 +00:00
|
|
|
if ($newLevel <= 0 || $newLevel >= $level) {
|
|
|
|
throw new SolverBugException(
|
|
|
|
"Trying to revert to invalid level ".(int) $newLevel." from level ".(int) $level."."
|
|
|
|
);
|
2012-05-21 16:34:12 +00:00
|
|
|
} elseif (!$newRule) {
|
2012-05-09 16:43:17 +00:00
|
|
|
throw new SolverBugException(
|
|
|
|
"No rule was learned from analyzing $rule at level $level."
|
|
|
|
);
|
|
|
|
}
|
2011-07-08 11:09:39 +00:00
|
|
|
|
|
|
|
$level = $newLevel;
|
|
|
|
|
|
|
|
$this->revert($level);
|
|
|
|
|
2012-05-15 19:36:47 +00:00
|
|
|
$this->rules->add($newRule, RuleSet::TYPE_LEARNED);
|
2011-07-08 11:09:39 +00:00
|
|
|
|
2011-08-20 17:45:20 +00:00
|
|
|
$this->learnedWhy[$newRule->getId()] = $why;
|
2011-07-08 11:09:39 +00:00
|
|
|
|
2012-05-18 23:27:57 +00:00
|
|
|
$ruleNode = new RuleWatchNode($newRule);
|
|
|
|
$ruleNode->watch2OnHighest($this->decisionMap);
|
|
|
|
$this->watchGraph->insert($ruleNode);
|
2011-07-08 11:09:39 +00:00
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
$this->decide($learnLiteral, $level, $newRule);
|
2011-07-08 11:09:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $level;
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private function selectAndInstall($level, array $decisionQueue, $disableRules, Rule $rule)
|
|
|
|
{
|
|
|
|
// choose best package to install from decisionQueue
|
2011-11-18 23:27:35 +00:00
|
|
|
$literals = $this->policy->selectPreferedPackages($this->pool, $this->installedMap, $decisionQueue);
|
2011-04-05 15:37:19 +00:00
|
|
|
|
2011-08-21 03:03:52 +00:00
|
|
|
$selectedLiteral = array_shift($literals);
|
|
|
|
|
2011-04-05 15:37:19 +00:00
|
|
|
// if there are multiple candidates, then branch
|
2011-08-21 03:03:52 +00:00
|
|
|
if (count($literals)) {
|
2012-02-22 15:19:05 +00:00
|
|
|
$this->branches[] = array($literals, $level);
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
|
|
|
|
2011-08-21 03:03:52 +00:00
|
|
|
return $this->setPropagateLearn($level, $selectedLiteral, $disableRules, $rule);
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
|
|
|
|
2011-08-20 17:20:17 +00:00
|
|
|
protected function analyze($level, $rule)
|
|
|
|
{
|
2012-05-09 16:43:17 +00:00
|
|
|
$analyzedRule = $rule;
|
2011-08-20 17:20:17 +00:00
|
|
|
$ruleLevel = 1;
|
|
|
|
$num = 0;
|
|
|
|
$l1num = 0;
|
|
|
|
$seen = array();
|
|
|
|
$learnedLiterals = array(null);
|
|
|
|
|
|
|
|
$decisionId = count($this->decisionQueue);
|
|
|
|
|
|
|
|
$this->learnedPool[] = array();
|
|
|
|
|
|
|
|
while(true) {
|
|
|
|
$this->learnedPool[count($this->learnedPool) - 1][] = $rule;
|
|
|
|
|
|
|
|
foreach ($rule->getLiterals() as $literal) {
|
|
|
|
// skip the one true literal
|
|
|
|
if ($this->decisionsSatisfy($literal)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
if (isset($seen[abs($literal)])) {
|
2011-08-20 17:20:17 +00:00
|
|
|
continue;
|
|
|
|
}
|
2012-05-19 18:38:56 +00:00
|
|
|
$seen[abs($literal)] = true;
|
2011-08-20 17:20:17 +00:00
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
$l = abs($this->decisionMap[abs($literal)]);
|
2011-08-20 17:20:17 +00:00
|
|
|
|
|
|
|
if (1 === $l) {
|
|
|
|
$l1num++;
|
2012-05-21 16:34:12 +00:00
|
|
|
} elseif ($level === $l) {
|
2011-08-20 17:20:17 +00:00
|
|
|
$num++;
|
|
|
|
} else {
|
|
|
|
// not level1 or conflict level, add to new rule
|
|
|
|
$learnedLiterals[] = $literal;
|
|
|
|
|
|
|
|
if ($l > $ruleLevel) {
|
|
|
|
$ruleLevel = $l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$l1retry = true;
|
|
|
|
while ($l1retry) {
|
|
|
|
$l1retry = false;
|
|
|
|
|
2012-02-17 23:09:25 +00:00
|
|
|
if (!$num && !--$l1num) {
|
2011-08-20 17:20:17 +00:00
|
|
|
// all level 1 literals done
|
|
|
|
break 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (true) {
|
2012-05-09 16:43:17 +00:00
|
|
|
if ($decisionId <= 0) {
|
|
|
|
throw new SolverBugException(
|
|
|
|
"Reached invalid decision id $decisionId while looking through $rule for a literal present in the analyzed rule $analyzedRule."
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2011-08-20 17:20:17 +00:00
|
|
|
$decisionId--;
|
|
|
|
|
|
|
|
$literal = $this->decisionQueue[$decisionId];
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
if (isset($seen[abs($literal)])) {
|
2011-08-20 17:20:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
unset($seen[abs($literal)]);
|
2011-08-20 17:20:17 +00:00
|
|
|
|
|
|
|
if ($num && 0 === --$num) {
|
2012-05-19 18:38:56 +00:00
|
|
|
$learnedLiterals[0] = -abs($literal);
|
2011-08-20 17:20:17 +00:00
|
|
|
|
|
|
|
if (!$l1num) {
|
|
|
|
break 2;
|
|
|
|
}
|
|
|
|
|
2011-12-06 20:13:04 +00:00
|
|
|
foreach ($learnedLiterals as $i => $learnedLiteral) {
|
2011-08-20 17:20:17 +00:00
|
|
|
if ($i !== 0) {
|
2012-05-19 18:38:56 +00:00
|
|
|
unset($seen[abs($learnedLiteral)]);
|
2011-08-20 17:20:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// only level 1 marks left
|
|
|
|
$l1num++;
|
|
|
|
$l1retry = true;
|
|
|
|
}
|
|
|
|
}
|
2012-04-27 17:41:53 +00:00
|
|
|
|
|
|
|
$rule = $this->decisionQueueWhy[$decisionId];
|
2011-08-20 17:20:17 +00:00
|
|
|
}
|
|
|
|
|
2011-08-20 17:45:20 +00:00
|
|
|
$why = count($this->learnedPool) - 1;
|
2012-04-27 17:41:53 +00:00
|
|
|
|
2012-05-09 16:43:17 +00:00
|
|
|
if (!$learnedLiterals[0]) {
|
|
|
|
throw new SolverBugException(
|
|
|
|
"Did not find a learnable literal in analyzed rule $analyzedRule."
|
|
|
|
);
|
2012-04-27 17:41:53 +00:00
|
|
|
}
|
2011-08-20 17:20:17 +00:00
|
|
|
|
2012-05-19 18:38:56 +00:00
|
|
|
$newRule = new Rule($this->pool, $learnedLiterals, Rule::RULE_LEARNED, $why);
|
2012-05-09 16:43:17 +00:00
|
|
|
|
2012-04-01 20:26:10 +00:00
|
|
|
return array($learnedLiterals[0], $ruleLevel, $newRule, $why);
|
2011-08-20 17:20:17 +00:00
|
|
|
}
|
|
|
|
|
2012-05-19 19:49:48 +00:00
|
|
|
private function analyzeUnsolvableRule($problem, $conflictRule)
|
2011-06-26 22:11:57 +00:00
|
|
|
{
|
2011-07-08 11:09:39 +00:00
|
|
|
$why = $conflictRule->getId();
|
2011-06-26 22:11:57 +00:00
|
|
|
|
2011-07-08 11:09:39 +00:00
|
|
|
if ($conflictRule->getType() == RuleSet::TYPE_LEARNED) {
|
2011-08-20 17:45:20 +00:00
|
|
|
$learnedWhy = $this->learnedWhy[$why];
|
2012-03-18 19:41:10 +00:00
|
|
|
$problemRules = $this->learnedPool[$learnedWhy];
|
2011-08-20 17:45:20 +00:00
|
|
|
|
2012-03-18 19:41:10 +00:00
|
|
|
foreach ($problemRules as $problemRule) {
|
2012-05-19 19:49:48 +00:00
|
|
|
$this->analyzeUnsolvableRule($problem, $problemRule);
|
2011-08-20 17:45:20 +00:00
|
|
|
}
|
|
|
|
return;
|
2011-07-08 11:09:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($conflictRule->getType() == RuleSet::TYPE_PACKAGE) {
|
|
|
|
// package rules cannot be part of a problem
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-05-15 19:36:47 +00:00
|
|
|
$problem->addRule($conflictRule);
|
2011-06-26 22:11:57 +00:00
|
|
|
}
|
2011-07-08 11:09:39 +00:00
|
|
|
|
|
|
|
private function analyzeUnsolvable($conflictRule, $disableRules)
|
2011-06-26 22:11:57 +00:00
|
|
|
{
|
2012-03-18 19:41:10 +00:00
|
|
|
$problem = new Problem;
|
|
|
|
$problem->addRule($conflictRule);
|
|
|
|
|
2012-05-19 19:49:48 +00:00
|
|
|
$this->analyzeUnsolvableRule($problem, $conflictRule);
|
2011-07-08 11:09:39 +00:00
|
|
|
|
2012-03-18 19:41:10 +00:00
|
|
|
$this->problems[] = $problem;
|
2011-07-08 11:09:39 +00:00
|
|
|
|
|
|
|
$seen = array();
|
|
|
|
$literals = $conflictRule->getLiterals();
|
|
|
|
|
|
|
|
foreach ($literals as $literal) {
|
|
|
|
// skip the one true literal
|
|
|
|
if ($this->decisionsSatisfy($literal)) {
|
|
|
|
continue;
|
|
|
|
}
|
2012-05-19 18:38:56 +00:00
|
|
|
$seen[abs($literal)] = true;
|
2011-07-08 11:09:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$decisionId = count($this->decisionQueue);
|
|
|
|
|
|
|
|
while ($decisionId > 0) {
|
|
|
|
$decisionId--;
|
|
|
|
|
|
|
|
$literal = $this->decisionQueue[$decisionId];
|
|
|
|
|
|
|
|
// skip literals that are not in this rule
|
2012-05-19 18:38:56 +00:00
|
|
|
if (!isset($seen[abs($literal)])) {
|
2011-07-08 11:09:39 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$why = $this->decisionQueueWhy[$decisionId];
|
2012-03-18 19:41:10 +00:00
|
|
|
$problem->addRule($why);
|
2011-07-08 11:09:39 +00:00
|
|
|
|
2012-05-19 19:49:48 +00:00
|
|
|
$this->analyzeUnsolvableRule($problem, $why);
|
2011-07-08 11:09:39 +00:00
|
|
|
|
|
|
|
$literals = $why->getLiterals();
|
|
|
|
|
|
|
|
foreach ($literals as $literal) {
|
|
|
|
// skip the one true literal
|
|
|
|
if ($this->decisionsSatisfy($literal)) {
|
|
|
|
continue;
|
|
|
|
}
|
2012-05-19 18:38:56 +00:00
|
|
|
$seen[abs($literal)] = true;
|
2011-07-08 11:09:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($disableRules) {
|
2012-03-18 19:41:10 +00:00
|
|
|
foreach ($this->problems[count($this->problems) - 1] as $reason) {
|
2012-05-15 19:36:47 +00:00
|
|
|
$this->disableProblem($reason['rule']);
|
2011-07-08 11:09:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$this->resetSolver();
|
2012-04-27 17:41:53 +00:00
|
|
|
return 1;
|
2011-07-08 11:09:39 +00:00
|
|
|
}
|
|
|
|
|
2012-04-27 17:41:53 +00:00
|
|
|
return 0;
|
2011-06-26 22:11:57 +00:00
|
|
|
}
|
2011-07-08 11:09:39 +00:00
|
|
|
|
|
|
|
private function disableProblem($why)
|
2011-06-26 22:11:57 +00:00
|
|
|
{
|
2012-05-15 19:36:47 +00:00
|
|
|
$job = $why->getJob();
|
2011-07-08 11:09:39 +00:00
|
|
|
|
2012-05-15 19:36:47 +00:00
|
|
|
if (!$job) {
|
|
|
|
$why->disable();
|
|
|
|
} else {
|
2011-07-08 11:09:39 +00:00
|
|
|
// disable all rules of this job
|
2012-05-15 19:36:47 +00:00
|
|
|
foreach ($this->rules as $rule) {
|
|
|
|
if ($job === $rule->getJob()) {
|
|
|
|
$rule->disable();
|
2011-07-08 11:09:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-06-26 22:11:57 +00:00
|
|
|
}
|
|
|
|
|
2011-07-08 11:09:39 +00:00
|
|
|
private function resetSolver()
|
2011-06-26 22:11:57 +00:00
|
|
|
{
|
2011-07-08 11:09:39 +00:00
|
|
|
while ($literal = array_pop($this->decisionQueue)) {
|
2012-05-19 18:38:56 +00:00
|
|
|
$this->decisionMap[abs($literal)] = 0;
|
2011-07-08 11:09:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$this->decisionQueueWhy = array();
|
|
|
|
$this->decisionQueueFree = array();
|
|
|
|
$this->propagateIndex = 0;
|
|
|
|
$this->branches = array();
|
|
|
|
|
|
|
|
$this->enableDisableLearnedRules();
|
|
|
|
$this->makeAssertionRuleDecisions();
|
2011-06-26 22:11:57 +00:00
|
|
|
}
|
|
|
|
|
2011-07-08 11:09:39 +00:00
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
* enable/disable learnt rules
|
|
|
|
*
|
|
|
|
* we have enabled or disabled some of our rules. We now reenable all
|
|
|
|
* of our learnt rules except the ones that were learnt from rules that
|
|
|
|
* are now disabled.
|
|
|
|
*/
|
|
|
|
private function enableDisableLearnedRules()
|
2011-06-07 20:43:26 +00:00
|
|
|
{
|
2011-07-08 11:09:39 +00:00
|
|
|
foreach ($this->rules->getIteratorFor(RuleSet::TYPE_LEARNED) as $rule) {
|
|
|
|
$why = $this->learnedWhy[$rule->getId()];
|
2012-03-18 19:41:10 +00:00
|
|
|
$problemRules = $this->learnedPool[$why];
|
2011-07-08 11:09:39 +00:00
|
|
|
|
|
|
|
$foundDisabled = false;
|
2012-03-18 19:41:10 +00:00
|
|
|
foreach ($problemRules as $problemRule) {
|
2012-04-16 11:46:04 +00:00
|
|
|
if ($problemRule->isDisabled()) {
|
2011-07-08 11:09:39 +00:00
|
|
|
$foundDisabled = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($foundDisabled && $rule->isEnabled()) {
|
|
|
|
$rule->disable();
|
2012-05-21 16:34:12 +00:00
|
|
|
} elseif (!$foundDisabled && $rule->isDisabled()) {
|
2011-07-08 11:09:39 +00:00
|
|
|
$rule->enable();
|
|
|
|
}
|
|
|
|
}
|
2011-06-07 20:43:26 +00:00
|
|
|
}
|
|
|
|
|
2012-05-15 18:01:51 +00:00
|
|
|
private function runSat($disableRules = true)
|
2011-04-05 15:37:19 +00:00
|
|
|
{
|
|
|
|
$this->propagateIndex = 0;
|
|
|
|
|
|
|
|
// /*
|
|
|
|
// * here's the main loop:
|
|
|
|
// * 1) propagate new decisions (only needed once)
|
|
|
|
// * 2) fulfill jobs
|
2012-04-27 17:41:53 +00:00
|
|
|
// * 3) fulfill all unresolved rules
|
|
|
|
// * 4) minimalize solution if we had choices
|
2011-04-05 15:37:19 +00:00
|
|
|
// * if we encounter a problem, we rewind to a safe level and restart
|
|
|
|
// * with step 1
|
|
|
|
// */
|
|
|
|
|
|
|
|
$decisionQueue = array();
|
|
|
|
$decisionSupplementQueue = array();
|
|
|
|
$disableRules = array();
|
|
|
|
|
|
|
|
$level = 1;
|
|
|
|
$systemLevel = $level + 1;
|
2012-01-11 08:11:27 +00:00
|
|
|
$minimizationSteps = 0;
|
2011-04-05 15:37:19 +00:00
|
|
|
$installedPos = 0;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
2011-11-21 14:02:42 +00:00
|
|
|
if (1 === $level) {
|
|
|
|
$conflictRule = $this->propagate($level);
|
|
|
|
if ($conflictRule !== null) {
|
|
|
|
if ($this->analyzeUnsolvable($conflictRule, $disableRules)) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// handle job rules
|
|
|
|
if ($level < $systemLevel) {
|
2011-06-26 22:11:57 +00:00
|
|
|
$iterator = $this->rules->getIteratorFor(RuleSet::TYPE_JOB);
|
|
|
|
foreach ($iterator as $rule) {
|
2011-04-05 15:37:19 +00:00
|
|
|
if ($rule->isEnabled()) {
|
|
|
|
$decisionQueue = array();
|
|
|
|
$noneSatisfied = true;
|
|
|
|
|
|
|
|
foreach ($rule->getLiterals() as $literal) {
|
|
|
|
if ($this->decisionsSatisfy($literal)) {
|
|
|
|
$noneSatisfied = false;
|
|
|
|
break;
|
|
|
|
}
|
2012-05-20 13:44:15 +00:00
|
|
|
if ($literal > 0 && $this->undecided($literal)) {
|
|
|
|
$decisionQueue[] = $literal;
|
|
|
|
}
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($noneSatisfied && count($decisionQueue)) {
|
|
|
|
// prune all update packages until installed version
|
|
|
|
// except for requested updates
|
|
|
|
if (count($this->installed) != count($this->updateMap)) {
|
|
|
|
$prunedQueue = array();
|
|
|
|
foreach ($decisionQueue as $literal) {
|
2012-05-19 18:38:56 +00:00
|
|
|
if (isset($this->installedMap[abs($literal)])) {
|
2011-04-05 15:37:19 +00:00
|
|
|
$prunedQueue[] = $literal;
|
2012-05-19 18:38:56 +00:00
|
|
|
if (isset($this->updateMap[abs($literal)])) {
|
2011-04-05 15:37:19 +00:00
|
|
|
$prunedQueue = $decisionQueue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$decisionQueue = $prunedQueue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($noneSatisfied && count($decisionQueue)) {
|
|
|
|
|
|
|
|
$oLevel = $level;
|
|
|
|
$level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule);
|
|
|
|
|
|
|
|
if (0 === $level) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ($level <= $oLevel) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$systemLevel = $level + 1;
|
|
|
|
|
|
|
|
// jobs left
|
2011-06-26 22:11:57 +00:00
|
|
|
$iterator->next();
|
|
|
|
if ($iterator->valid()) {
|
2011-04-05 15:37:19 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($level < $systemLevel) {
|
|
|
|
$systemLevel = $level;
|
|
|
|
}
|
|
|
|
|
2011-07-08 11:09:39 +00:00
|
|
|
for ($i = 0, $n = 0; $n < count($this->rules); $i++, $n++) {
|
|
|
|
if ($i == count($this->rules)) {
|
|
|
|
$i = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
$rule = $this->rules->ruleById($i);
|
|
|
|
$literals = $rule->getLiterals();
|
|
|
|
|
|
|
|
if ($rule->isDisabled()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$decisionQueue = array();
|
|
|
|
|
|
|
|
// make sure that
|
|
|
|
// * all negative literals are installed
|
|
|
|
// * no positive literal is installed
|
|
|
|
// i.e. the rule is not fulfilled and we
|
|
|
|
// just need to decide on the positive literals
|
|
|
|
//
|
|
|
|
foreach ($literals as $literal) {
|
2012-05-19 18:38:56 +00:00
|
|
|
if ($literal <= 0) {
|
|
|
|
if (!$this->decidedInstall(abs($literal))) {
|
2011-07-08 11:09:39 +00:00
|
|
|
continue 2; // next rule
|
|
|
|
}
|
|
|
|
} else {
|
2012-05-19 18:38:56 +00:00
|
|
|
if ($this->decidedInstall(abs($literal))) {
|
2011-07-08 11:09:39 +00:00
|
|
|
continue 2; // next rule
|
|
|
|
}
|
2012-05-19 18:38:56 +00:00
|
|
|
if ($this->undecided(abs($literal))) {
|
2011-07-08 11:09:39 +00:00
|
|
|
$decisionQueue[] = $literal;
|
|
|
|
}
|
|
|
|
}
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
2011-07-08 11:09:39 +00:00
|
|
|
|
|
|
|
// need to have at least 2 item to pick from
|
|
|
|
if (count($decisionQueue) < 2) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$oLevel = $level;
|
|
|
|
$level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule);
|
|
|
|
|
|
|
|
if (0 === $level) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// something changed, so look at all rules again
|
|
|
|
$n = -1;
|
2011-04-05 15:37:19 +00:00
|
|
|
}
|
2011-07-08 11:09:39 +00:00
|
|
|
|
2012-04-27 17:41:53 +00:00
|
|
|
if ($level < $systemLevel) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-08-21 03:03:52 +00:00
|
|
|
// minimization step
|
2011-07-08 11:09:39 +00:00
|
|
|
if (count($this->branches)) {
|
2011-08-21 03:03:52 +00:00
|
|
|
|
|
|
|
$lastLiteral = null;
|
|
|
|
$lastLevel = null;
|
|
|
|
$lastBranchIndex = 0;
|
|
|
|
$lastBranchOffset = 0;
|
|
|
|
|
|
|
|
for ($i = count($this->branches) - 1; $i >= 0; $i--) {
|
|
|
|
list($literals, $level) = $this->branches[$i];
|
|
|
|
|
|
|
|
foreach ($literals as $offset => $literal) {
|
2012-05-19 18:38:56 +00:00
|
|
|
if ($literal && $literal > 0 && $this->decisionMap[abs($literal)] > $level + 1) {
|
2011-08-21 03:03:52 +00:00
|
|
|
$lastLiteral = $literal;
|
|
|
|
$lastBranchIndex = $i;
|
|
|
|
$lastBranchOffset = $offset;
|
|
|
|
$lastLevel = $level;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($lastLiteral) {
|
|
|
|
$this->branches[$lastBranchIndex][$lastBranchOffset] = null;
|
|
|
|
$minimizationSteps++;
|
|
|
|
|
|
|
|
$level = $lastLevel;
|
|
|
|
$this->revert($level);
|
|
|
|
|
2012-02-22 15:19:05 +00:00
|
|
|
$why = $this->decisionQueueWhy[count($this->decisionQueueWhy) - 1];
|
2011-08-21 03:03:52 +00:00
|
|
|
|
|
|
|
$oLevel = $level;
|
|
|
|
$level = $this->setPropagateLearn($level, $lastLiteral, $disableRules, $why);
|
|
|
|
|
|
|
|
if ($level == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
2011-07-08 11:09:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-09-23 23:29:22 +00:00
|
|
|
}
|