Solver.php 71 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073
  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Composer\DependencyResolver;
  12. use Composer\Repository\RepositoryInterface;
  13. use Composer\Package\PackageInterface;
  14. /**
  15. * @author Nils Adermann <naderman@naderman.de>
  16. */
  17. class Solver
  18. {
  19. const RULE_INTERNAL_ALLOW_UPDATE = 1;
  20. const RULE_JOB_INSTALL = 2;
  21. const RULE_JOB_REMOVE = 3;
  22. const RULE_JOB_LOCK = 4;
  23. const RULE_NOT_INSTALLABLE = 5;
  24. const RULE_NOTHING_PROVIDES_DEP = 6;
  25. const RULE_PACKAGE_CONFLICT = 7;
  26. const RULE_PACKAGE_NOT_EXIST = 8;
  27. const RULE_PACKAGE_REQUIRES = 9;
  28. const RULE_PACKAGE_OBSOLETES = 10;
  29. const RULE_INSTALLED_PACKAGE_OBSOLETES = 11;
  30. const RULE_PACKAGE_SAME_NAME = 12;
  31. const RULE_PACKAGE_IMPLICIT_OBSOLETES = 13;
  32. const RULE_LEARNED = 14;
  33. protected $policy;
  34. protected $pool;
  35. protected $installed;
  36. protected $rules;
  37. protected $updateAll;
  38. protected $ruleToJob = array();
  39. protected $addedMap = array();
  40. protected $fixMap = array();
  41. protected $updateMap = array();
  42. protected $noObsoletes = array();
  43. protected $watches = array();
  44. protected $removeWatches = array();
  45. protected $packageToUpdateRule = array();
  46. protected $packageToFeatureRule = array();
  47. public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed)
  48. {
  49. $this->policy = $policy;
  50. $this->pool = $pool;
  51. $this->installed = $installed;
  52. $this->rules = new RuleSet;
  53. }
  54. /**
  55. * Creates a new rule for the requirements of a package
  56. *
  57. * This rule is of the form (-A|B|C), where B and C are the providers of
  58. * one requirement of the package A.
  59. *
  60. * @param PackageInterface $package The package with a requirement
  61. * @param array $providers The providers of the requirement
  62. * @param int $reason A RULE_* constant describing the
  63. * reason for generating this rule
  64. * @param mixed $reasonData Any data, e.g. the requirement name,
  65. * that goes with the reason
  66. * @return Rule The generated rule or null if tautological
  67. */
  68. public function createRequireRule(PackageInterface $package, array $providers, $reason, $reasonData = null)
  69. {
  70. $literals = array(new Literal($package, false));
  71. foreach ($providers as $provider) {
  72. // self fulfilling rule?
  73. if ($provider === $package) {
  74. return null;
  75. }
  76. $literals[] = new Literal($provider, true);
  77. }
  78. return new Rule($literals, $reason, $reasonData);
  79. }
  80. /**
  81. * Create a new rule for updating a package
  82. *
  83. * If package A1 can be updated to A2 or A3 the rule is (A1|A2|A3).
  84. *
  85. * @param PackageInterface $package The package to be updated
  86. * @param array $updates An array of update candidate packages
  87. * @param int $reason A RULE_* constant describing the
  88. * reason for generating this rule
  89. * @param mixed $reasonData Any data, e.g. the package name, that
  90. * goes with the reason
  91. * @return Rule The generated rule or null if tautology
  92. */
  93. protected function createUpdateRule(PackageInterface $package, array $updates, $reason, $reasonData = null)
  94. {
  95. $literals = array(new Literal($package, true));
  96. foreach ($updates as $update) {
  97. $literals[] = new Literal($update, true);
  98. }
  99. return new Rule($literals, $reason, $reasonData);
  100. }
  101. /**
  102. * Creates a new rule for installing a package
  103. *
  104. * The rule is simply (A) for a package A to be installed.
  105. *
  106. * @param PackageInterface $package The package to be installed
  107. * @param int $reason A RULE_* constant describing the
  108. * reason for generating this rule
  109. * @param mixed $reasonData Any data, e.g. the package name, that
  110. * goes with the reason
  111. * @return Rule The generated rule
  112. */
  113. public function createInstallRule(PackageInterface $package, $reason, $reasonData = null)
  114. {
  115. return new Rule(new Literal($package, true));
  116. }
  117. /**
  118. * Creates a rule to install at least one of a set of packages
  119. *
  120. * The rule is (A|B|C) with A, B and C different packages. If the given
  121. * set of packages is empty an impossible rule is generated.
  122. *
  123. * @param array $packages The set of packages to choose from
  124. * @param int $reason A RULE_* constant describing the reason for
  125. * generating this rule
  126. * @param mixed $reasonData Any data, e.g. the package name, that goes with
  127. * the reason
  128. * @return Rule The generated rule
  129. */
  130. public function createInstallOneOfRule(array $packages, $reason, $reasonData = null)
  131. {
  132. if (empty($packages)) {
  133. return $this->createImpossibleRule($reason, $reasonData);
  134. }
  135. $literals = array();
  136. foreach ($packages as $package) {
  137. $literals[] = new Literal($package, true);
  138. }
  139. return new Rule($literals, $reason, $reasonData);
  140. }
  141. /**
  142. * Creates a rule to remove a package
  143. *
  144. * The rule for a package A is (-A).
  145. *
  146. * @param PackageInterface $package The package to be removed
  147. * @param int $reason A RULE_* constant describing the
  148. * reason for generating this rule
  149. * @param mixed $reasonData Any data, e.g. the package name, that
  150. * goes with the reason
  151. * @return Rule The generated rule
  152. */
  153. public function createRemoveRule(PackageInterface $package, $reason, $reasonData = null)
  154. {
  155. return new Rule(array(new Literal($package, false)), $reason, $reasonData);
  156. }
  157. /**
  158. * Creates a rule for two conflicting packages
  159. *
  160. * The rule for conflicting packages A and B is (-A|-B). A is called the issuer
  161. * and B the provider.
  162. *
  163. * @param PackageInterface $issuer The package declaring the conflict
  164. * @param Package $provider The package causing the conflict
  165. * @param int $reason A RULE_* constant describing the
  166. * reason for generating this rule
  167. * @param mixed $reasonData Any data, e.g. the package name, that
  168. * goes with the reason
  169. * @return Rule The generated rule
  170. */
  171. public function createConflictRule(PackageInterface $issuer, PackageInterface $provider, $reason, $reasonData = null)
  172. {
  173. // ignore self conflict
  174. if ($issuer === $provider) {
  175. return null;
  176. }
  177. return new Rule(array(new Literal($issuer, false), new Literal($provider, false)), $reason, $reasonData);
  178. }
  179. /**
  180. * Intentionally creates a rule impossible to solve
  181. *
  182. * The rule is an empty one so it can never be satisfied.
  183. *
  184. * @param int $reason A RULE_* constant describing the reason for
  185. * generating this rule
  186. * @param mixed $reasonData Any data, e.g. the package name, that goes with
  187. * the reason
  188. * @return Rule An empty rule
  189. */
  190. public function createImpossibleRule($reason, $reasonData = null)
  191. {
  192. return new Rule(array(), $reason, $reasonData);
  193. }
  194. /**
  195. * Adds a rule unless it duplicates an existing one of any type
  196. *
  197. * To be able to directly pass in the result of one of the rule creation
  198. * methods the rule may also be null to indicate that no rule should be
  199. * added.
  200. *
  201. * @param int $type A TYPE_* constant defining the rule type
  202. * @param Rule $newRule The rule about to be added
  203. */
  204. private function addRule($type, Rule $newRule = null) {
  205. if ($newRule) {
  206. foreach ($this->rules->getIterator() as $rule) {
  207. if ($rule->equals($newRule)) {
  208. return;
  209. }
  210. }
  211. $this->rules->add($newRule, $type);
  212. }
  213. }
  214. public function addRulesForPackage(PackageInterface $package)
  215. {
  216. $workQueue = new \SPLQueue;
  217. $workQueue->enqueue($package);
  218. while (!$workQueue->isEmpty()) {
  219. $package = $workQueue->dequeue();
  220. if (isset($this->addedMap[$package->getId()])) {
  221. continue;
  222. }
  223. $this->addedMap[$package->getId()] = true;
  224. $dontFix = 0;
  225. if ($this->installed === $package->getRepository() && !isset($this->fixMap[$package->getId()])) {
  226. $dontFix = 1;
  227. }
  228. if (!$dontFix && !$this->policy->installable($this, $this->pool, $this->installed, $package)) {
  229. $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRemoveRule($package, self::RULE_NOT_INSTALLABLE, (string) $package));
  230. continue;
  231. }
  232. foreach ($package->getRequires() as $link) {
  233. $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
  234. // the strategy here is to not insist on dependencies
  235. // that are already broken. so if we find one provider
  236. // that was already installed, we know that the
  237. // dependency was not broken before so we enforce it
  238. if ($dontFix) {
  239. $foundInstalled = false;
  240. foreach ($possibleRequires as $require) {
  241. if ($this->installed === $require->getRepository()) {
  242. $foundInstalled = true;
  243. break;
  244. }
  245. }
  246. // no installed provider found: previously broken dependency => don't add rule
  247. if (!$foundInstalled) {
  248. continue;
  249. }
  250. }
  251. $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, $possibleRequires, self::RULE_PACKAGE_REQUIRES, (string) $link));
  252. foreach ($possibleRequires as $require) {
  253. $workQueue->enqueue($require);
  254. }
  255. }
  256. foreach ($package->getConflicts() as $link) {
  257. $possibleConflicts = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
  258. foreach ($possibleConflicts as $conflict) {
  259. if ($dontFix && $this->installed === $conflict->getRepository()) {
  260. continue;
  261. }
  262. $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $conflict, self::RULE_PACKAGE_CONFLICT, (string) $link));
  263. }
  264. }
  265. // check obsoletes and implicit obsoletes of a package
  266. // if ignoreinstalledsobsoletes is not set, we're also checking
  267. // obsoletes of installed packages (like newer rpm versions)
  268. //
  269. /** @TODO: if ($this->noInstalledObsoletes) */
  270. if (true) {
  271. $noObsoletes = isset($this->noObsoletes[$package->getId()]);
  272. $isInstalled = ($this->installed === $package->getRepository());
  273. foreach ($package->getReplaces() as $link) {
  274. $obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
  275. foreach ($obsoleteProviders as $provider) {
  276. if ($provider === $package) {
  277. continue;
  278. }
  279. if ($isInstalled && $dontFix && $this->installed === $provider->getRepository()) {
  280. continue; // don't repair installed/installed problems
  281. }
  282. $reason = ($isInstalled) ? self::RULE_INSTALLED_PACKAGE_OBSOLETES : self::RULE_PACKAGE_OBSOLETES;
  283. $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link));
  284. }
  285. }
  286. // check implicit obsoletes
  287. // for installed packages we only need to check installed/installed problems (and
  288. // only when dontFix is not set), as the others are picked up when looking at the
  289. // uninstalled package.
  290. if (!$isInstalled || !$dontFix) {
  291. $obsoleteProviders = $this->pool->whatProvides($package->getName(), null);
  292. foreach ($obsoleteProviders as $provider) {
  293. if ($provider === $package) {
  294. continue;
  295. }
  296. if ($isInstalled && $this->installed !== $provider->getRepository()) {
  297. continue;
  298. }
  299. // obsolete same packages even when noObsletes
  300. if ($noObsoletes && (!$package->equals($provider))) {
  301. continue;
  302. }
  303. $reason = ($package->getName() == $provider->getName()) ? self::RULE_PACKAGE_SAME_NAME : self::RULE_PACKAGE_IMPLICIT_OBSOLETES;
  304. $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, (string) $package));
  305. }
  306. }
  307. }
  308. foreach ($package->getRecommends() as $link) {
  309. foreach ($this->pool->whatProvides($link->getTarget(), $link->getConstraint()) as $recommend) {
  310. $workQueue->enqueue($recommend);
  311. }
  312. }
  313. foreach ($package->getSuggests() as $link) {
  314. foreach ($this->pool->whatProvides($link->getTarget(), $link->getConstraint()) as $suggest) {
  315. $workQueue->enqueue($suggest);
  316. }
  317. }
  318. }
  319. }
  320. /**
  321. * Adds all rules for all update packages of a given package
  322. *
  323. * @param PackageInterface $package Rules for this package's updates are to
  324. * be added
  325. * @param bool $allowAll Whether downgrades are allowed
  326. */
  327. private function addRulesForUpdatePackages(PackageInterface $package, $allowAll)
  328. {
  329. $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installed, $package, $allowAll);
  330. $this->addRulesForPackage($package);
  331. foreach ($updates as $update) {
  332. $this->addRulesForPackage($update);
  333. }
  334. }
  335. /**
  336. * Alters watch chains for a rule.
  337. *
  338. * Next1/2 always points to the next rule that is watching the same package.
  339. * The watches array contains rules to start from for each package
  340. *
  341. */
  342. private function addWatchesToRule(Rule $rule)
  343. {
  344. // skip simple assertions of the form (A) or (-A)
  345. if ($rule->isAssertion()) {
  346. return;
  347. }
  348. if (!isset($this->watches[$rule->watch1])) {
  349. $this->watches[$rule->watch1] = null;
  350. }
  351. $rule->next1 = $this->watches[$rule->watch1];
  352. $this->watches[$rule->watch1] = $rule;
  353. if (!isset($this->watches[$rule->watch2])) {
  354. $this->watches[$rule->watch2] = null;
  355. }
  356. $rule->next2 = $this->watches[$rule->watch2];
  357. $this->watches[$rule->watch2] = $rule;
  358. }
  359. /**
  360. * Put watch2 on rule's literal with highest level
  361. */
  362. private function watch2OnHighest(Rule $rule)
  363. {
  364. $literals = $rule->getLiterals();
  365. // if there are only 2 elements, both are being watched anyway
  366. if ($literals < 3) {
  367. return;
  368. }
  369. $watchLevel = 0;
  370. foreach ($literals as $literal) {
  371. if (isset($this->decisionMap[$literal->getPackageId()])) {
  372. $level = abs($this->decisionMap[$literal->getPackageId()]);
  373. } else {
  374. $level = 0;
  375. }
  376. if ($level > $watchLevel) {
  377. $rule->watch2 = $literal->getId();
  378. $watchLevel = $level;
  379. }
  380. }
  381. }
  382. private function findDecisionRule(PackageInterface $package)
  383. {
  384. foreach ($this->decisionQueue as $i => $literal) {
  385. if ($package === $literal->getPackage()) {
  386. return $this->decisionQueueWhy[$i];
  387. }
  388. }
  389. return null;
  390. }
  391. // aka solver_makeruledecisions
  392. private function makeAssertionRuleDecisions()
  393. {
  394. // do we need to decide a SYSTEMSOLVABLE at level 1?
  395. $decisionStart = count($this->decisionQueue);
  396. for ($ruleIndex = 0; $ruleIndex < count($this->rules); $ruleIndex++) {
  397. $rule = $this->rules->ruleById($ruleIndex);
  398. if ($rule->isWeak() || !$rule->isAssertion() || $rule->isDisabled()) {
  399. continue;
  400. }
  401. $literals = $rule->getLiterals();
  402. $literal = $literals[0];
  403. if (!$this->decided($literal->getPackage())) {
  404. $this->decisionQueue[] = $literal;
  405. $this->decisionQueueWhy[] = $rule;
  406. $this->addDecision($literal, 1);
  407. continue;
  408. }
  409. if ($this->decisionsSatisfy($literal)) {
  410. continue;
  411. }
  412. // found a conflict
  413. if (RuleSet::TYPE_LEARNED === $rule->getType()) {
  414. $rule->disable();
  415. continue;
  416. }
  417. $conflict = $this->findDecisionRule($literal->getPackage());
  418. /** TODO: handle conflict with systemsolvable? */
  419. $this->learnedPool[] = array($rule, $conflict);
  420. if ($conflict && RuleSet::TYPE_PACKAGE === $conflict->getType()) {
  421. if ($rule->getType() == RuleSet::TYPE_JOB) {
  422. $why = $this->ruleToJob[$rule->getId()];
  423. } else {
  424. $why = $rule;
  425. }
  426. $this->problems[] = array($why);
  427. $this->disableProblem($why);
  428. continue;
  429. }
  430. // conflict with another job or update/feature rule
  431. $this->problems[] = array();
  432. // push all of our rules (can only be feature or job rules)
  433. // asserting this literal on the problem stack
  434. foreach ($this->rules->getIteratorFor(array(RuleSet::TYPE_JOB, RuleSet::TYPE_UPDATE, RuleSet::TYPE_FEATURE)) as $assertRule) {
  435. if ($assertRule->isDisabled() || !$assertRule->isAssertion() || $assertRule->isWeak()) {
  436. continue;
  437. }
  438. $assertRuleLiterals = $assertRule->getLiterals();
  439. $assertRuleLiteral = $assertRuleLiterals[0];
  440. if ($literal->getPackageId() !== $assertRuleLiteral->getPackageId()) {
  441. continue;
  442. }
  443. if ($assertRule->getType() === RuleSet::TYPE_JOB) {
  444. $why = $this->ruleToJob[$assertRule->getId()];
  445. } else {
  446. $why = $assertRule;
  447. }
  448. $this->problems[count($this->problems) - 1][] = $why;
  449. $this->disableProblem($why);
  450. }
  451. // start over
  452. while (count($this->decisionQueue) > $decisionStart) {
  453. $decisionLiteral = array_pop($this->decisionQueue);
  454. array_pop($this->decisionQueueWhy);
  455. unset($this->decisionQueueFree[count($this->decisionQueue)]);
  456. unset($this->decisionMap[$decisionLiteral->getPackageId()]);
  457. }
  458. $ruleIndex = -1;
  459. }
  460. foreach ($this->rules as $rule) {
  461. if (!$rule->isWeak() || !$rule->isAssertion() || $rule->isDisabled()) {
  462. continue;
  463. }
  464. $literals = $rule->getLiterals();
  465. $literal = $literals[0];
  466. if (!isset($this->decisionMap[$literal->getPackageId()])) {
  467. $this->decisionQueue[] = $literal;
  468. $this->decisionQueueWhy[] = $rule;
  469. $this->addDecision($literal, 1);
  470. continue;
  471. }
  472. if ($this->decisionsSatisfy($literals[0])) {
  473. continue;
  474. }
  475. // conflict, but this is a weak rule => disable
  476. if ($rule->getType() == RuleSet::TYPE_JOB) {
  477. $why = $this->ruleToJob[$rule->getId()];
  478. } else {
  479. $why = $rule;
  480. }
  481. $this->disableProblem($why);
  482. /** TODO solver_reenablepolicyrules(solv, -(v + 1)); */
  483. }
  484. }
  485. public function addChoiceRules()
  486. {
  487. // void
  488. // solver_addchoicerules(Solver *solv)
  489. // {
  490. // Pool *pool = solv->pool;
  491. // Map m, mneg;
  492. // Rule *r;
  493. // Queue q, qi;
  494. // int i, j, rid, havechoice;
  495. // Id p, d, *pp;
  496. // Id p2, pp2;
  497. // Solvable *s, *s2;
  498. //
  499. // solv->choicerules = solv->nrules;
  500. // if (!pool->installed)
  501. // {
  502. // solv->choicerules_end = solv->nrules;
  503. // return;
  504. // }
  505. // solv->choicerules_ref = sat_calloc(solv->rpmrules_end, sizeof(Id));
  506. // queue_init(&q);
  507. // queue_init(&qi);
  508. // map_init(&m, pool->nsolvables);
  509. // map_init(&mneg, pool->nsolvables);
  510. // /* set up negative assertion map from infarch and dup rules */
  511. // for (rid = solv->infarchrules, r = solv->rules + rid; rid < solv->infarchrules_end; rid++, r++)
  512. // if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1))
  513. // MAPSET(&mneg, -r->p);
  514. // for (rid = solv->duprules, r = solv->rules + rid; rid < solv->duprules_end; rid++, r++)
  515. // if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1))
  516. // MAPSET(&mneg, -r->p);
  517. // for (rid = 1; rid < solv->rpmrules_end ; rid++)
  518. // {
  519. // r = solv->rules + rid;
  520. // if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 < 0))
  521. // continue; /* only look at requires rules */
  522. // // solver_printrule(solv, SAT_DEBUG_RESULT, r);
  523. // queue_empty(&q);
  524. // queue_empty(&qi);
  525. // havechoice = 0;
  526. // FOR_RULELITERALS(p, pp, r)
  527. // {
  528. // if (p < 0)
  529. // continue;
  530. // s = pool->solvables + p;
  531. // if (!s->repo)
  532. // continue;
  533. // if (s->repo == pool->installed)
  534. // {
  535. // queue_push(&q, p);
  536. // continue;
  537. // }
  538. // /* check if this package is "blocked" by a installed package */
  539. // s2 = 0;
  540. // FOR_PROVIDES(p2, pp2, s->name)
  541. // {
  542. // s2 = pool->solvables + p2;
  543. // if (s2->repo != pool->installed)
  544. // continue;
  545. // if (!pool->implicitobsoleteusesprovides && s->name != s2->name)
  546. // continue;
  547. // if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
  548. // continue;
  549. // break;
  550. // }
  551. // if (p2)
  552. // {
  553. // /* found installed package p2 that we can update to p */
  554. // if (MAPTST(&mneg, p))
  555. // continue;
  556. // if (policy_is_illegal(solv, s2, s, 0))
  557. // continue;
  558. // queue_push(&qi, p2);
  559. // queue_push(&q, p);
  560. // continue;
  561. // }
  562. // if (s->obsoletes)
  563. // {
  564. // Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
  565. // s2 = 0;
  566. // while ((obs = *obsp++) != 0)
  567. // {
  568. // FOR_PROVIDES(p2, pp2, obs)
  569. // {
  570. // s2 = pool->solvables + p2;
  571. // if (s2->repo != pool->installed)
  572. // continue;
  573. // if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
  574. // continue;
  575. // if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
  576. // continue;
  577. // break;
  578. // }
  579. // if (p2)
  580. // break;
  581. // }
  582. // if (obs)
  583. // {
  584. // /* found installed package p2 that we can update to p */
  585. // if (MAPTST(&mneg, p))
  586. // continue;
  587. // if (policy_is_illegal(solv, s2, s, 0))
  588. // continue;
  589. // queue_push(&qi, p2);
  590. // queue_push(&q, p);
  591. // continue;
  592. // }
  593. // }
  594. // /* package p is independent of the installed ones */
  595. // havechoice = 1;
  596. // }
  597. // if (!havechoice || !q.count)
  598. // continue; /* no choice */
  599. //
  600. // /* now check the update rules of the installed package.
  601. // * if all packages of the update rules are contained in
  602. // * the dependency rules, there's no need to set up the choice rule */
  603. // map_empty(&m);
  604. // FOR_RULELITERALS(p, pp, r)
  605. // if (p > 0)
  606. // MAPSET(&m, p);
  607. // for (i = 0; i < qi.count; i++)
  608. // {
  609. // if (!qi.elements[i])
  610. // continue;
  611. // Rule *ur = solv->rules + solv->updaterules + (qi.elements[i] - pool->installed->start);
  612. // if (!ur->p)
  613. // ur = solv->rules + solv->featurerules + (qi.elements[i] - pool->installed->start);
  614. // if (!ur->p)
  615. // continue;
  616. // FOR_RULELITERALS(p, pp, ur)
  617. // if (!MAPTST(&m, p))
  618. // break;
  619. // if (p)
  620. // break;
  621. // for (j = i + 1; j < qi.count; j++)
  622. // if (qi.elements[i] == qi.elements[j])
  623. // qi.elements[j] = 0;
  624. // }
  625. // if (i == qi.count)
  626. // {
  627. // #if 0
  628. // printf("skipping choice ");
  629. // solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + rid);
  630. // #endif
  631. // continue;
  632. // }
  633. // d = q.count ? pool_queuetowhatprovides(pool, &q) : 0;
  634. // solver_addrule(solv, r->p, d);
  635. // queue_push(&solv->weakruleq, solv->nrules - 1);
  636. // solv->choicerules_ref[solv->nrules - 1 - solv->choicerules] = rid;
  637. // #if 0
  638. // printf("OLD ");
  639. // solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + rid);
  640. // printf("WEAK CHOICE ");
  641. // solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + solv->nrules - 1);
  642. // #endif
  643. // }
  644. // queue_free(&q);
  645. // queue_free(&qi);
  646. // map_free(&m);
  647. // map_free(&mneg);
  648. // solv->choicerules_end = solv->nrules;
  649. // }
  650. }
  651. /***********************************************************************
  652. ***
  653. *** Policy rule disabling/reenabling
  654. ***
  655. *** Disable all policy rules that conflict with our jobs. If a job
  656. *** gets disabled later on, reenable the involved policy rules again.
  657. ***
  658. *** /
  659. #define DISABLE_UPDATE 1
  660. #define DISABLE_INFARCH 2
  661. #define DISABLE_DUP 3
  662. */
  663. protected function jobToDisableQueue(array $job, array $disableQueue)
  664. {
  665. switch ($job['cmd']) {
  666. case 'install':
  667. foreach ($job['packages'] as $package) {
  668. if ($this->installed === $package->getRepository()) {
  669. $disableQueue[] = array('type' => 'update', 'package' => $package);
  670. }
  671. /* all job packages obsolete * /
  672. qstart = q->count;
  673. pass = 0;
  674. memset(&omap, 0, sizeof(omap));
  675. FOR_JOB_SELECT(p, pp, select, what)
  676. {
  677. Id p2, pp2;
  678. if (pass == 1)
  679. map_grow(&omap, installed->end - installed->start);
  680. s = pool->solvables + p;
  681. if (s->obsoletes)
  682. {
  683. Id obs, *obsp;
  684. obsp = s->repo->idarraydata + s->obsoletes;
  685. while ((obs = *obsp++) != 0)
  686. FOR_PROVIDES(p2, pp2, obs)
  687. {
  688. Solvable *ps = pool->solvables + p2;
  689. if (ps->repo != installed)
  690. continue;
  691. if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
  692. continue;
  693. if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
  694. continue;
  695. if (pass)
  696. MAPSET(&omap, p2 - installed->start);
  697. else
  698. queue_push2(q, DISABLE_UPDATE, p2);
  699. }
  700. }
  701. FOR_PROVIDES(p2, pp2, s->name)
  702. {
  703. Solvable *ps = pool->solvables + p2;
  704. if (ps->repo != installed)
  705. continue;
  706. if (!pool->implicitobsoleteusesprovides && ps->name != s->name)
  707. continue;
  708. if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
  709. continue;
  710. if (pass)
  711. MAPSET(&omap, p2 - installed->start);
  712. else
  713. queue_push2(q, DISABLE_UPDATE, p2);
  714. }
  715. if (pass)
  716. {
  717. for (i = j = qstart; i < q->count; i += 2)
  718. {
  719. if (MAPTST(&omap, q->elements[i + 1] - installed->start))
  720. {
  721. MAPCLR(&omap, q->elements[i + 1] - installed->start);
  722. q->elements[j + 1] = q->elements[i + 1];
  723. j += 2;
  724. }
  725. }
  726. queue_truncate(q, j);
  727. }
  728. if (q->count == qstart)
  729. break;
  730. pass++;
  731. }
  732. if (omap.size)
  733. map_free(&omap);
  734. if (qstart == q->count)
  735. return; /* nothing to prune * /
  736. if ((set & (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) == (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR))
  737. return; /* all is set */
  738. /* now that we know which installed packages are obsoleted check each of them * /
  739. for (i = j = qstart; i < q->count; i += 2)
  740. {
  741. Solvable *is = pool->solvables + q->elements[i + 1];
  742. FOR_JOB_SELECT(p, pp, select, what)
  743. {
  744. int illegal = 0;
  745. s = pool->solvables + p;
  746. if ((set & SOLVER_SETEVR) != 0)
  747. illegal |= POLICY_ILLEGAL_DOWNGRADE; /* ignore * /
  748. if ((set & SOLVER_SETARCH) != 0)
  749. illegal |= POLICY_ILLEGAL_ARCHCHANGE; /* ignore * /
  750. if ((set & SOLVER_SETVENDOR) != 0)
  751. illegal |= POLICY_ILLEGAL_VENDORCHANGE; /* ignore * /
  752. illegal = policy_is_illegal(solv, is, s, illegal);
  753. if (illegal && illegal == POLICY_ILLEGAL_DOWNGRADE && (set & SOLVER_SETEV) != 0)
  754. {
  755. /* it's ok if the EV is different * /
  756. if (evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE_EVONLY) != 0)
  757. illegal = 0;
  758. }
  759. if (illegal)
  760. break;
  761. }
  762. if (!p)
  763. {
  764. /* no package conflicts with the update rule * /
  765. /* thus keep the DISABLE_UPDATE * /
  766. q->elements[j + 1] = q->elements[i + 1];
  767. j += 2;
  768. }
  769. }
  770. queue_truncate(q, j);
  771. return;*/
  772. }
  773. break;
  774. case 'remove':
  775. foreach ($job['packages'] as $package) {
  776. if ($this->installed === $package->getRepository()) {
  777. $disableQueue[] = array('type' => 'update', 'package' => $package);
  778. }
  779. }
  780. break;
  781. }
  782. return $disableQueue;
  783. }
  784. protected function disableUpdateRule($package)
  785. {
  786. // find update & feature rule and disable
  787. if (isset($this->packageToUpdateRule[$package->getId()])) {
  788. $this->packageToUpdateRule[$package->getId()]->disable();
  789. }
  790. if (isset($this->packageToFeatureRule[$package->getId()])) {
  791. $this->packageToFeatureRule[$literal->getPackageId()]->disable();
  792. }
  793. }
  794. /**
  795. * Disables all policy rules that conflict with jobs
  796. */
  797. protected function disablePolicyRules()
  798. {
  799. $lastJob = null;
  800. $allQueue = array();
  801. $iterator = $this->rules->getIteratorFor(RuleSet::TYPE_JOB);
  802. foreach ($iterator as $rule) {
  803. if ($rule->isDisabled()) {
  804. continue;
  805. }
  806. $job = $this->ruleToJob[$rule->getId()];
  807. if ($job === $lastJob) {
  808. continue;
  809. }
  810. $lastJob = $job;
  811. $allQueue = $this->jobToDisableQueue($job, $allQueue);
  812. }
  813. foreach ($allQueue as $disable) {
  814. switch ($disable['type']) {
  815. case 'update':
  816. $this->disableUpdateRule($disable['package']);
  817. break;
  818. default:
  819. throw new \RuntimeException("Unsupported disable type: " . $disable['type']);
  820. }
  821. }
  822. }
  823. public function solve(Request $request)
  824. {
  825. $this->jobs = $request->getJobs();
  826. $installedPackages = $this->installed->getPackages();
  827. foreach ($this->jobs as $job) {
  828. switch ($job['cmd']) {
  829. case 'update-all':
  830. foreach ($installedPackages as $package) {
  831. $this->updateMap[$package->getId()] = true;
  832. }
  833. break;
  834. case 'fix-all':
  835. foreach ($installedPackages as $package) {
  836. $this->fixMap[$package->getId()] = true;
  837. }
  838. break;
  839. }
  840. foreach ($job['packages'] as $package) {
  841. switch ($job['cmd']) {
  842. case 'fix':
  843. if ($this->installed === $package->getRepository()) {
  844. $this->fixMap[$package->getId()] = true;
  845. }
  846. break;
  847. case 'update':
  848. if ($this->installed === $package->getRepository()) {
  849. $this->updateMap[$package->getId()] = true;
  850. }
  851. break;
  852. }
  853. }
  854. }
  855. foreach ($installedPackages as $package) {
  856. $this->addRulesForPackage($package);
  857. }
  858. foreach ($installedPackages as $package) {
  859. $this->addRulesForUpdatePackages($package, true);
  860. }
  861. foreach ($this->jobs as $job) {
  862. foreach ($job['packages'] as $package) {
  863. switch ($job['cmd']) {
  864. case 'install':
  865. $this->installCandidateMap[$package->getId()] = true;
  866. $this->addRulesForPackage($package);
  867. break;
  868. }
  869. }
  870. }
  871. // solver_addrpmrulesforweak(solv, &addedmap);
  872. foreach ($installedPackages as $package) {
  873. // create a feature rule which allows downgrades
  874. $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installed, $package, true);
  875. $featureRule = $this->createUpdateRule($package, $updates, self::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
  876. // create an update rule which does not allow downgrades
  877. $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installed, $package, false);
  878. $rule = $this->createUpdateRule($package, $updates, self::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
  879. if ($rule->equals($featureRule)) {
  880. if ($this->policy->allowUninstall()) {
  881. $featureRule->setWeak(true);
  882. $this->addRule(RuleSet::TYPE_FEATURE, $featureRule);
  883. $this->packageToFeatureRule[$package->getId()] = $rule;
  884. } else {
  885. $this->addRule(RuleSet::TYPE_UPDATE, $rule);
  886. $this->packageToUpdateRule[$package->getId()] = $rule;
  887. }
  888. } else if ($this->policy->allowUninstall()) {
  889. $featureRule->setWeak(true);
  890. $rule->setWeak(true);
  891. $this->addRule(RuleSet::TYPE_FEATURE, $featureRule);
  892. $this->addRule(RuleSet::TYPE_UPDATE, $rule);
  893. $this->packageToFeatureRule[$package->getId()] = $rule;
  894. $this->packageToUpdateRule[$package->getId()] = $rule;
  895. }
  896. }
  897. foreach ($this->jobs as $job) {
  898. switch ($job['cmd']) {
  899. case 'install':
  900. $rule = $this->createInstallOneOfRule($job['packages'], self::RULE_JOB_INSTALL, $job['packageName']);
  901. $this->addRule(RuleSet::TYPE_JOB, $rule);
  902. $this->ruleToJob[$rule->getId()] = $job;
  903. break;
  904. case 'remove':
  905. // remove all packages with this name including uninstalled
  906. // ones to make sure none of them are picked as replacements
  907. // todo: cleandeps
  908. foreach ($job['packages'] as $package) {
  909. $rule = $this->createRemoveRule($package, self::RULE_JOB_REMOVE);
  910. $this->addRule(RuleSet::TYPE_JOB, $rule);
  911. $this->ruleToJob[$rule->getId()] = $job;
  912. }
  913. break;
  914. case 'lock':
  915. foreach ($job['packages'] as $package) {
  916. if ($this->installed === $package->getRepository()) {
  917. $rule = $this->createInstallRule($package, self::RULE_JOB_LOCK);
  918. } else {
  919. $rule = $this->createRemoveRule($package, self::RULE_JOB_LOCK);
  920. }
  921. $this->addRule(RuleSet::TYPE_JOB, $rule);
  922. $this->ruleToJob[$rule->getId()] = $job;
  923. }
  924. break;
  925. }
  926. }
  927. $this->addChoiceRules();
  928. foreach ($this->rules as $rule) {
  929. $this->addWatchesToRule($rule);
  930. }
  931. /* disable update rules that conflict with our job */
  932. $this->disablePolicyRules();
  933. /* make decisions based on job/update assertions */
  934. $this->makeAssertionRuleDecisions();
  935. $installRecommended = 0;
  936. $this->runSat(true, $installRecommended);
  937. //$this->printDecisionMap();
  938. //findrecommendedsuggested(solv);
  939. //solver_prepare_solutions(solv);
  940. return $this->createTransaction();
  941. }
  942. protected function createTransaction()
  943. {
  944. $transaction = array();
  945. $installMeansUpdateMap = array();
  946. $ignoreRemoveMap = array();
  947. foreach ($this->decisionQueue as $i => $literal) {
  948. $package = $literal->getPackage();
  949. // !wanted & installed
  950. if (!$literal->isWanted() && $this->installed === $package->getRepository()) {
  951. $updateRule = $this->packageToUpdateRule[$package->getId()];
  952. foreach ($updateRule->getLiterals() as $updateLiteral) {
  953. if (!$updateLiteral->equals($literal)) {
  954. $installMeansUpdateMap[$updateLiteral->getPackageId()] = $package;
  955. }
  956. }
  957. }
  958. }
  959. foreach ($this->decisionQueue as $i => $literal) {
  960. $package = $literal->getPackage();
  961. // wanted & installed || !wanted & !installed
  962. if ($literal->isWanted() == ($this->installed === $package->getRepository())) {
  963. continue;
  964. }
  965. if ($literal->isWanted()) {
  966. if (isset($installMeansUpdateMap[$literal->getPackageId()])) {
  967. $source = $installMeansUpdateMap[$literal->getPackageId()];
  968. $transaction[] = array(
  969. 'job' => 'update',
  970. 'from' => $source,
  971. 'to' => $package,
  972. 'why' => $this->decisionQueueWhy[$i],
  973. );
  974. // avoid updates to one package from multiple origins
  975. unset($installMeansUpdateMap[$literal->getPackageId()]);
  976. $ignoreRemove[$source->getId()] = true;
  977. } else {
  978. $transaction[] = array(
  979. 'job' => 'install',
  980. 'package' => $package,
  981. 'why' => $this->decisionQueueWhy[$i],
  982. );
  983. }
  984. } else if (!isset($ignoreRemove[$package->getId()])) {
  985. $transaction[] = array(
  986. 'job' => 'remove',
  987. 'package' => $package,
  988. 'why' => $this->decisionQueueWhy[$i],
  989. );
  990. }
  991. }
  992. return array_reverse($transaction);
  993. }
  994. protected $decisionQueue = array();
  995. protected $decisionQueueWhy = array();
  996. protected $decisionQueueFree = array();
  997. protected $propagateIndex;
  998. protected $decisionMap = array();
  999. protected $branches = array();
  1000. protected $problems = array();
  1001. protected $learnedPool = array();
  1002. protected $recommendsIndex;
  1003. protected function literalFromId($id)
  1004. {
  1005. $package = $this->pool->packageById(abs($id));
  1006. return new Literal($package, $id > 0);
  1007. }
  1008. protected function addDecision(Literal $l, $level)
  1009. {
  1010. if ($l->isWanted()) {
  1011. $this->decisionMap[$l->getPackageId()] = $level;
  1012. } else {
  1013. $this->decisionMap[$l->getPackageId()] = -$level;
  1014. }
  1015. }
  1016. protected function addDecisionId($literalId, $level)
  1017. {
  1018. $packageId = abs($literalId);
  1019. if ($literalId > 0) {
  1020. $this->decisionMap[$packageId] = $level;
  1021. } else {
  1022. $this->decisionMap[$packageId] = -$level;
  1023. }
  1024. }
  1025. protected function decisionsContain(Literal $l)
  1026. {
  1027. return (isset($this->decisionMap[$l->getPackageId()]) && (
  1028. $this->decisionMap[$l->getPackageId()] > 0 && $l->isWanted() ||
  1029. $this->decisionMap[$l->getPackageId()] < 0 && !$l->isWanted()
  1030. ));
  1031. }
  1032. protected function decisionsContainId($literalId)
  1033. {
  1034. $packageId = abs($literalId);
  1035. return (isset($this->decisionMap[$packageId]) && (
  1036. $this->decisionMap[$packageId] > 0 && $literalId > 0 ||
  1037. $this->decisionMap[$packageId] < 0 && $literalId < 0
  1038. ));
  1039. }
  1040. protected function decisionsSatisfy(Literal $l)
  1041. {
  1042. return ($l->isWanted() && isset($this->decisionMap[$l->getPackageId()]) && $this->decisionMap[$l->getPackageId()] > 0) ||
  1043. (!$l->isWanted() && (!isset($this->decisionMap[$l->getPackageId()]) || $this->decisionMap[$l->getPackageId()] < 0));
  1044. }
  1045. protected function decisionsConflict(Literal $l)
  1046. {
  1047. return (isset($this->decisionMap[$l->getPackageId()]) && (
  1048. $this->decisionMap[$l->getPackageId()] > 0 && !$l->isWanted() ||
  1049. $this->decisionMap[$l->getPackageId()] < 0 && $l->isWanted()
  1050. ));
  1051. }
  1052. protected function decisionsConflictId($literalId)
  1053. {
  1054. $packageId = abs($literalId);
  1055. return (isset($this->decisionMap[$packageId]) && (
  1056. $this->decisionMap[$packageId] > 0 && !($literalId < 0) ||
  1057. $this->decisionMap[$packageId] < 0 && $literalId > 0
  1058. ));
  1059. }
  1060. protected function decided(PackageInterface $p)
  1061. {
  1062. return isset($this->decisionMap[$p->getId()]);
  1063. }
  1064. protected function undecided(PackageInterface $p)
  1065. {
  1066. return !isset($this->decisionMap[$p->getId()]);
  1067. }
  1068. protected function decidedInstall(PackageInterface $p) {
  1069. return isset($this->decisionMap[$p->getId()]) && $this->decisionMap[$p->getId()] > 0;
  1070. }
  1071. protected function decidedRemove(PackageInterface $p) {
  1072. return isset($this->decisionMap[$p->getId()]) && $this->decisionMap[$p->getId()] < 0;
  1073. }
  1074. /**
  1075. * Makes a decision and propagates it to all rules.
  1076. *
  1077. * Evaluates each term affected by the decision (linked through watches)
  1078. * If we find unit rules we make new decisions based on them
  1079. *
  1080. * @return Rule|null A rule on conflict, otherwise null.
  1081. */
  1082. protected function propagate($level)
  1083. {
  1084. while ($this->propagateIndex < count($this->decisionQueue)) {
  1085. // we invert the decided literal here, example:
  1086. // A was decided => (-A|B) now requires B to be true, so we look for
  1087. // rules which are fulfilled by -A, rather than A.
  1088. $literal = $this->decisionQueue[$this->propagateIndex]->inverted();
  1089. $this->propagateIndex++;
  1090. // /* foreach rule where 'pkg' is now FALSE */
  1091. //for (rp = watches + pkg; *rp; rp = next_rp)
  1092. if (!isset($this->watches[$literal->getId()])) {
  1093. continue;
  1094. }
  1095. for ($rule = $this->watches[$literal->getId()]; $rule !== null; $rule = $nextRule) {
  1096. $nextRule = $rule->getNext($literal);
  1097. if ($rule->isDisabled()) {
  1098. continue;
  1099. }
  1100. $otherWatch = $rule->getOtherWatch($literal);
  1101. if ($this->decisionsContainId($otherWatch)) {
  1102. continue;
  1103. }
  1104. $ruleLiterals = $rule->getLiterals();
  1105. if (sizeof($ruleLiterals) > 2) {
  1106. foreach ($ruleLiterals as $ruleLiteral) {
  1107. if (!$otherWatch->equals($ruleLiteral) &&
  1108. !$this->decisionsConflict($ruleLiteral)) {
  1109. if ($literal->equals($rule->getWatch1())) {
  1110. $rule->setWatch1($ruleLiteral);
  1111. $rule->setNext1($rule);
  1112. } else {
  1113. $rule->setWatch2($ruleLiteral);
  1114. $rule->setNext2($rule);
  1115. }
  1116. $this->watches[$ruleLiteral->getId()] = $rule;
  1117. continue 2;
  1118. }
  1119. }
  1120. }
  1121. // yay, we found a unit clause! try setting it to true
  1122. if ($this->decisionsConflictId($otherWatch)) {
  1123. return $rule;
  1124. }
  1125. $this->addDecisionId($otherWatch, $level);
  1126. $this->decisionQueue[] = $this->literalFromId($otherWatch);
  1127. $this->decisionQueueWhy[] = $rule;
  1128. }
  1129. }
  1130. return null;
  1131. }
  1132. /**
  1133. * Reverts a decision at the given level.
  1134. */
  1135. private function revert($level)
  1136. {
  1137. while (!empty($this->decisionQueue)) {
  1138. $literal = $this->decisionQueue[count($this->decisionQueue) - 1];
  1139. if (!isset($this->decisionMap[$literal->getPackageId()])) {
  1140. break;
  1141. }
  1142. $decisionLevel = abs($this->decisionMap[$literal->getPackageId()]);
  1143. if ($decisionLevel <= $level) {
  1144. break;
  1145. }
  1146. /** TODO: implement recommendations
  1147. *if (v > 0 && solv->recommendations.count && v == solv->recommendations.elements[solv->recommendations.count - 1])
  1148. * solv->recommendations.count--;
  1149. */
  1150. unset($this->decisionMap[$literal->getPackageId()]);
  1151. array_pop($this->decisionQueue);
  1152. array_pop($this->decisionQueueWhy);
  1153. $this->propagateIndex = count($this->decisionQueue);
  1154. }
  1155. while (!empty($this->branches)) {
  1156. list($literals, $branchLevel) = $this->branches[count($this->branches) - 1];
  1157. if ($branchLevel > -$level) {
  1158. continue;
  1159. }
  1160. array_pop($this->branches);
  1161. }
  1162. $this->recommendsIndex = -1;
  1163. }
  1164. /**-------------------------------------------------------------------
  1165. *
  1166. * setpropagatelearn
  1167. *
  1168. * add free decision (solvable to install) to decisionq
  1169. * increase level and propagate decision
  1170. * return if no conflict.
  1171. *
  1172. * in conflict case, analyze conflict rule, add resulting
  1173. * rule to learnt rule set, make decision from learnt
  1174. * rule (always unit) and re-propagate.
  1175. *
  1176. * returns the new solver level or 0 if unsolvable
  1177. *
  1178. */
  1179. private function setPropagateLearn($level, Literal $literal, $disableRules, Rule $rule)
  1180. {
  1181. assert($rule != null);
  1182. assert($literal != null);
  1183. $level++;
  1184. $this->addDecision($literal, $level);
  1185. $this->decisionQueue[] = $literal;
  1186. $this->decisionQueueWhy[] = $rule;
  1187. $this->decisionQueueFree[count($this->decisionQueueWhy) - 1] = true;
  1188. while (true) {
  1189. $rule = $this->propagate($level);
  1190. if (!$rule) {
  1191. break;
  1192. }
  1193. if ($level == 1) {
  1194. return $this->analyzeUnsolvable($rule, $disableRules);
  1195. }
  1196. // conflict
  1197. list($newLevel, $newRule, $why) = $this->analyze($level, $rule);
  1198. assert($newLevel > 0);
  1199. assert($newLevel < $level);
  1200. $level = $newLevel;
  1201. $this->revert($level);
  1202. assert($newRule != null);
  1203. $this->addRule(RuleSet::TYPE_LEARNED, $newRule);
  1204. $this->learnedWhy[$newRule->getId()] = $why;
  1205. $this->watch2OnHighest($newRule);
  1206. $this->addWatchesToRule($newRule);
  1207. $literals = $newRule->getLiterals();
  1208. $this->addDecision($literals[0], $level);
  1209. $this->decisionQueue[] = $literals[0];
  1210. $this->decisionQueueWhy[] = $newRule;
  1211. }
  1212. return $level;
  1213. }
  1214. private function selectAndInstall($level, array $decisionQueue, $disableRules, Rule $rule)
  1215. {
  1216. // choose best package to install from decisionQueue
  1217. $literals = $this->policy->selectPreferedPackages($this, $this->pool, $this->installed, $decisionQueue);
  1218. // if there are multiple candidates, then branch
  1219. if (count($literals) > 1) {
  1220. foreach ($literals as $i => $literal) {
  1221. if (0 !== $i) {
  1222. $this->branches[] = array(array($literal), -$level);
  1223. }
  1224. }
  1225. }
  1226. return $this->setPropagateLearn($level, $literals[0], $disableRules, $rule);
  1227. }
  1228. protected function analyze($level, $rule)
  1229. {
  1230. $ruleLevel = 1;
  1231. $num = 0;
  1232. $l1num = 0;
  1233. $seen = array();
  1234. $learnedLiterals = array(null);
  1235. $decisionId = count($this->decisionQueue);
  1236. $this->learnedPool[] = array();
  1237. while(true) {
  1238. $this->learnedPool[count($this->learnedPool) - 1][] = $rule;
  1239. foreach ($rule->getLiterals() as $literal) {
  1240. // skip the one true literal
  1241. if ($this->decisionsSatisfy($literal)) {
  1242. continue;
  1243. }
  1244. if (isset($seen[$literal->getPackageId()])) {
  1245. continue;
  1246. }
  1247. $seen[$literal->getPackageId()] = true;
  1248. $l = abs($this->decisionMap[$literal->getPackageId()]);
  1249. if (1 === $l) {
  1250. $l1num++;
  1251. } else if ($level === $l) {
  1252. $num++;
  1253. } else {
  1254. // not level1 or conflict level, add to new rule
  1255. $learnedLiterals[] = $literal;
  1256. if ($l > $ruleLevel) {
  1257. $ruleLevel = $l;
  1258. }
  1259. }
  1260. }
  1261. $l1retry = true;
  1262. while ($l1retry) {
  1263. $l1retry = false;
  1264. if (!$num && !$l1num) {
  1265. // all level 1 literals done
  1266. break 2;
  1267. }
  1268. while (true) {
  1269. assert($decisionId > 0);
  1270. $decisionId--;
  1271. $literal = $this->decisionQueue[$decisionId];
  1272. if (isset($seen[$literal->getPackageId()])) {
  1273. break;
  1274. }
  1275. }
  1276. unset($seen[$literal->getPackageId()]);
  1277. if ($num && 0 === --$num) {
  1278. $learnedLiterals[0] = $this->literalFromId(-$literal->getPackageId());
  1279. if (!$l1num) {
  1280. break 2;
  1281. }
  1282. foreach ($this->learnedLiterals as $i => $learnedLiteral) {
  1283. if ($i !== 0) {
  1284. unset($seen[$literal->getPackageId()]);
  1285. }
  1286. }
  1287. // only level 1 marks left
  1288. $l1num++;
  1289. $l1retry = true;
  1290. }
  1291. $rule = $this->decisionQueueWhy[$decisionId];
  1292. }
  1293. }
  1294. $why = count($this->learnedPool) - 1;
  1295. $newRule = new Rule($learnedLiterals, self::RULE_LEARNED, $why);
  1296. return array($ruleLevel, $newRule, $why);
  1297. }
  1298. private function analyzeUnsolvableRule($conflictRule, &$lastWeakWhy)
  1299. {
  1300. $why = $conflictRule->getId();
  1301. if ($conflictRule->getType() == RuleSet::TYPE_LEARNED) {
  1302. $learnedWhy = $this->learnedWhy[$why];
  1303. $problem = $this->learnedPool[$learnedWhy];
  1304. foreach ($problem as $problemRule) {
  1305. $this->analyzeUnsolvableRule($problemRule, $lastWeakWhy);
  1306. }
  1307. return;
  1308. }
  1309. if ($conflictRule->getType() == RuleSet::TYPE_PACKAGE) {
  1310. // package rules cannot be part of a problem
  1311. return;
  1312. }
  1313. if ($conflictRule->isWeak()) {
  1314. /** TODO why > or < lastWeakProblem? */
  1315. if (!$lastWeakWhy || $why > $lastWeakWhy) {
  1316. $lastWeakProblem = $why;
  1317. }
  1318. }
  1319. if ($conflictRule->getType() == RuleSet::TYPE_JOB) {
  1320. $why = $this->ruleToJob[$conflictRule->getId()];
  1321. }
  1322. // if this problem was already found skip it
  1323. if (in_array($why, $this->problems[count($this->problems) - 1], true)) {
  1324. return;
  1325. }
  1326. $this->problems[count($this->problems) - 1][] = $why;
  1327. }
  1328. private function analyzeUnsolvable($conflictRule, $disableRules)
  1329. {
  1330. $lastWeakWhy = null;
  1331. $this->problems[] = array();
  1332. $this->learnedPool[] = array($conflictRule);
  1333. $this->analyzeUnsolvableRule($conflictRule, $lastWeakWhy);
  1334. $seen = array();
  1335. $literals = $conflictRule->getLiterals();
  1336. /* unecessary because unlike rule.d, watch2 == 2nd literal, unless watch2 changed
  1337. if (sizeof($literals) == 2) {
  1338. $literals[1] = $this->literalFromId($conflictRule->watch2);
  1339. }
  1340. */
  1341. foreach ($literals as $literal) {
  1342. // skip the one true literal
  1343. if ($this->decisionsSatisfy($literal)) {
  1344. continue;
  1345. }
  1346. $seen[$literal->getPackageId()] = true;
  1347. }
  1348. $decisionId = count($this->decisionQueue);
  1349. while ($decisionId > 0) {
  1350. $decisionId--;
  1351. $literal = $this->decisionQueue[$decisionId];
  1352. // skip literals that are not in this rule
  1353. if (!isset($seen[$literal->getPackageId()])) {
  1354. continue;
  1355. }
  1356. $why = $this->decisionQueueWhy[$decisionId];
  1357. $this->learnedPool[count($this->learnedPool) - 1][] = $why;
  1358. $this->analyzeUnsolvableRule($why, $lastWeakWhy);
  1359. $literals = $why->getLiterals();
  1360. /* unecessary because unlike rule.d, watch2 == 2nd literal, unless watch2 changed
  1361. if (sizeof($literals) == 2) {
  1362. $literals[1] = $this->literalFromId($why->watch2);
  1363. }
  1364. */
  1365. foreach ($literals as $literal) {
  1366. // skip the one true literal
  1367. if ($this->decisionsSatisfy($literal)) {
  1368. continue;
  1369. }
  1370. $seen[$literal->getPackageId()] = true;
  1371. }
  1372. }
  1373. if ($lastWeakWhy) {
  1374. array_pop($this->problems);
  1375. array_pop($this->learnedPool);
  1376. if ($lastWeakWhy->getType() === RuleSet::TYPE_JOB) {
  1377. $why = $this->ruleToJob[$lastWeakWhy];
  1378. } else {
  1379. $why = $lastWeakWhy;
  1380. }
  1381. if ($lastWeakWhy->getType() == RuleSet::TYPE_CHOICE) {
  1382. $this->disableChoiceRules($lastWeakWhy);
  1383. }
  1384. $this->disableProblem($why);
  1385. /**
  1386. @TODO what does v < 0 mean here? ($why == v)
  1387. if (v < 0)
  1388. solver_reenablepolicyrules(solv, -(v + 1));
  1389. */
  1390. $this->resetSolver();
  1391. return true;
  1392. }
  1393. if ($disableRules) {
  1394. foreach ($this->problems[count($this->problems) - 1] as $why) {
  1395. $this->disableProblem($why);
  1396. }
  1397. $this->resetSolver();
  1398. return true;
  1399. }
  1400. return false;
  1401. }
  1402. private function disableProblem($why)
  1403. {
  1404. if ($why instanceof Rule) {
  1405. $why->disable();
  1406. } else if (is_array($why)) {
  1407. // disable all rules of this job
  1408. foreach ($this->ruleToJob as $ruleId => $job) {
  1409. if ($why === $job) {
  1410. $this->rules->ruleById($ruleId)->disable();
  1411. }
  1412. }
  1413. }
  1414. }
  1415. private function resetSolver()
  1416. {
  1417. while ($literal = array_pop($this->decisionQueue)) {
  1418. if (isset($this->decisionMap[$literal->getPackageId()])) {
  1419. unset($this->decisionMap[$literal->getPackageId()]);
  1420. }
  1421. }
  1422. $this->decisionQueueWhy = array();
  1423. $this->decisionQueueFree = array();
  1424. $this->recommendsIndex = -1;
  1425. $this->propagateIndex = 0;
  1426. $this->recommendations = array();
  1427. $this->branches = array();
  1428. $this->enableDisableLearnedRules();
  1429. $this->makeAssertionRuleDecisions();
  1430. }
  1431. /*-------------------------------------------------------------------
  1432. * enable/disable learnt rules
  1433. *
  1434. * we have enabled or disabled some of our rules. We now reenable all
  1435. * of our learnt rules except the ones that were learnt from rules that
  1436. * are now disabled.
  1437. */
  1438. private function enableDisableLearnedRules()
  1439. {
  1440. foreach ($this->rules->getIteratorFor(RuleSet::TYPE_LEARNED) as $rule) {
  1441. $why = $this->learnedWhy[$rule->getId()];
  1442. $problem = $this->learnedPool[$why];
  1443. $foundDisabled = false;
  1444. foreach ($problem as $problemRule) {
  1445. if ($problemRule->disabled()) {
  1446. $foundDisabled = true;
  1447. break;
  1448. }
  1449. }
  1450. if ($foundDisabled && $rule->isEnabled()) {
  1451. $rule->disable();
  1452. } else if (!$foundDisabled && $rule->isDisabled()) {
  1453. $rule->enable();
  1454. }
  1455. }
  1456. }
  1457. private function runSat($disableRules = true, $installRecommended = false)
  1458. {
  1459. $this->propagateIndex = 0;
  1460. // /*
  1461. // * here's the main loop:
  1462. // * 1) propagate new decisions (only needed once)
  1463. // * 2) fulfill jobs
  1464. // * 3) try to keep installed packages
  1465. // * 4) fulfill all unresolved rules
  1466. // * 5) install recommended packages
  1467. // * 6) minimalize solution if we had choices
  1468. // * if we encounter a problem, we rewind to a safe level and restart
  1469. // * with step 1
  1470. // */
  1471. $decisionQueue = array();
  1472. $decisionSupplementQueue = array();
  1473. $disableRules = array();
  1474. $level = 1;
  1475. $systemLevel = $level + 1;
  1476. $minimizationsteps = 0;
  1477. $installedPos = 0;
  1478. $this->installedPackages = $this->installed->getPackages();
  1479. while (true) {
  1480. $conflictRule = $this->propagate($level);
  1481. if ($conflictRule !== null) {
  1482. if ($this->analyzeUnsolvable($conflictRule, $disableRules)) {
  1483. continue;
  1484. } else {
  1485. return;
  1486. }
  1487. }
  1488. // handle job rules
  1489. if ($level < $systemLevel) {
  1490. $iterator = $this->rules->getIteratorFor(RuleSet::TYPE_JOB);
  1491. foreach ($iterator as $rule) {
  1492. if ($rule->isEnabled()) {
  1493. $decisionQueue = array();
  1494. $noneSatisfied = true;
  1495. foreach ($rule->getLiterals() as $literal) {
  1496. if ($this->decisionsSatisfy($literal)) {
  1497. $noneSatisfied = false;
  1498. break;
  1499. }
  1500. $decisionQueue[] = $literal;
  1501. }
  1502. if ($noneSatisfied && count($decisionQueue)) {
  1503. // prune all update packages until installed version
  1504. // except for requested updates
  1505. if (count($this->installed) != count($this->updateMap)) {
  1506. $prunedQueue = array();
  1507. foreach ($decisionQueue as $literal) {
  1508. if ($this->installed === $literal->getPackage()->getRepository()) {
  1509. $prunedQueue[] = $literal;
  1510. if (isset($this->updateMap[$literal->getPackageId()])) {
  1511. $prunedQueue = $decisionQueue;
  1512. break;
  1513. }
  1514. }
  1515. }
  1516. $decisionQueue = $prunedQueue;
  1517. }
  1518. }
  1519. if ($noneSatisfied && count($decisionQueue)) {
  1520. $oLevel = $level;
  1521. $level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule);
  1522. if (0 === $level) {
  1523. return;
  1524. }
  1525. if ($level <= $oLevel) {
  1526. break;
  1527. }
  1528. }
  1529. }
  1530. }
  1531. $systemLevel = $level + 1;
  1532. // jobs left
  1533. $iterator->next();
  1534. if ($iterator->valid()) {
  1535. continue;
  1536. }
  1537. }
  1538. // handle installed packages
  1539. if ($level < $systemLevel) {
  1540. // use two passes if any packages are being updated
  1541. // -> better user experience
  1542. for ($pass = (count($this->updateMap)) ? 0 : 1; $pass < 2; $pass++) {
  1543. $passLevel = $level;
  1544. for ($i = $installedPos, $n = 0; $n < count($this->installedPackages); $i++, $n++) {
  1545. $repeat = false;
  1546. if ($i == count($this->installedPackages)) {
  1547. $i = 0;
  1548. }
  1549. $literal = new Literal($this->installedPackages[$i], true);
  1550. if ($this->decisionsContain($literal)) {
  1551. continue;
  1552. }
  1553. // only process updates in first pass
  1554. /** TODO: && or || ? **/
  1555. if (0 === $pass && !isset($this->updateMap[$literal->getPackageId()])) {
  1556. continue;
  1557. }
  1558. $rule = null;
  1559. if (isset($this->packageToUpdateRule[$literal->getPackageId()])) {
  1560. $rule = $this->packageToUpdateRule[$literal->getPackageId()];
  1561. }
  1562. if ((!$rule || $rule->isDisabled()) && isset($this->packageToFeatureRule[$literal->getPackageId()])) {
  1563. $rule = $this->packageToFeatureRule[$literal->getPackageId()];
  1564. }
  1565. if (!$rule || $rule->isDisabled()) {
  1566. continue;
  1567. }
  1568. $updateRuleLiterals = $rule->getLiterals();
  1569. $decisionQueue = array();
  1570. if (!isset($this->noUpdate[$literal->getPackageId()]) && (
  1571. $this->decidedRemove($literal->getPackage()) ||
  1572. isset($this->updateMap[$literal->getPackageId()]) ||
  1573. !$literal->equals($updateRuleLiterals[0])
  1574. )) {
  1575. foreach ($updateRuleLiterals as $ruleLiteral) {
  1576. if ($this->decidedInstall($ruleLiteral->getPackage())) {
  1577. // already fulfilled
  1578. $decisionQueue = array();
  1579. break;
  1580. }
  1581. if ($this->undecided($ruleLiteral->getPackage())) {
  1582. $decisionQueue[] = $ruleLiteral;
  1583. }
  1584. }
  1585. }
  1586. if (sizeof($decisionQueue)) {
  1587. $oLevel = $level;
  1588. $level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule);
  1589. if (0 === $level) {
  1590. return;
  1591. }
  1592. if ($level <= $oLevel) {
  1593. $repeat = true;
  1594. }
  1595. } else if (!$repeat && $this->undecided($literal->getPackage())) {
  1596. // still undecided? keep package.
  1597. $oLevel = $level;
  1598. if (isset($this->cleanDepsMap[$literal->getPackageId()])) {
  1599. // clean deps removes package
  1600. $level = $this->setPropagateLearn($level, $literal->invert(), $disableRules, null);
  1601. } else {
  1602. // ckeeping package
  1603. $level = $this->setPropagateLearn($level, $literal, $disableRules, $rule);
  1604. }
  1605. if (0 === $level) {
  1606. return;
  1607. }
  1608. if ($level <= $oLevel) {
  1609. $repeat = true;
  1610. }
  1611. }
  1612. if ($repeat) {
  1613. if (1 === $level || $level < $passLevel) {
  1614. // trouble
  1615. break;
  1616. }
  1617. if ($level < $oLevel) {
  1618. // redo all
  1619. $n = 0;
  1620. }
  1621. // repeat
  1622. $i--;
  1623. $n--;
  1624. continue;
  1625. }
  1626. }
  1627. if ($n < count($this->installedPackages)) {
  1628. $installedPos = $i; // retry this problem next time
  1629. break;
  1630. }
  1631. $installedPos = 0;
  1632. }
  1633. $systemlevel = $level + 1;
  1634. if ($pass < 2) {
  1635. // had trouble => retry
  1636. continue;
  1637. }
  1638. }
  1639. if ($level < $systemLevel) {
  1640. $systemLevel = $level;
  1641. }
  1642. for ($i = 0, $n = 0; $n < count($this->rules); $i++, $n++) {
  1643. if ($i == count($this->rules)) {
  1644. $i = 0;
  1645. }
  1646. $rule = $this->rules->ruleById($i);
  1647. $literals = $rule->getLiterals();
  1648. if ($rule->isDisabled()) {
  1649. continue;
  1650. }
  1651. $decisionQueue = array();
  1652. // make sure that
  1653. // * all negative literals are installed
  1654. // * no positive literal is installed
  1655. // i.e. the rule is not fulfilled and we
  1656. // just need to decide on the positive literals
  1657. //
  1658. foreach ($literals as $literal) {
  1659. if (!$literal->isWanted()) {
  1660. if (!$this->decidedInstall($literal->getPackage())) {
  1661. continue 2; // next rule
  1662. }
  1663. } else {
  1664. if ($this->decidedInstall($literal->getPackage())) {
  1665. continue 2; // next rule
  1666. }
  1667. if ($this->undecided($literal->getPackage())) {
  1668. $decisionQueue[] = $literal;
  1669. }
  1670. }
  1671. }
  1672. // need to have at least 2 item to pick from
  1673. if (count($decisionQueue) < 2) {
  1674. continue;
  1675. }
  1676. $oLevel = $level;
  1677. $level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule);
  1678. if (0 === $level) {
  1679. return;
  1680. }
  1681. if ($level < $systemLevel || $level == 1) {
  1682. break; // trouble
  1683. }
  1684. // something changed, so look at all rules again
  1685. $n = -1;
  1686. }
  1687. // $this->printDecisionMap();
  1688. // $this->printDecisionQueue();
  1689. if (count($this->branches)) {
  1690. die("minimization unimplemented");
  1691. // /* minimization step */
  1692. // if (solv->branches.count)
  1693. // {
  1694. // int l = 0, lasti = -1, lastl = -1;
  1695. // Id why;
  1696. //
  1697. // p = 0;
  1698. // for (i = solv->branches.count - 1; i >= 0; i--)
  1699. // {
  1700. // p = solv->branches.elements[i];
  1701. // if (p < 0)
  1702. // l = -p;
  1703. // else if (p > 0 && solv->decisionmap[p] > l + 1)
  1704. // {
  1705. // lasti = i;
  1706. // lastl = l;
  1707. // }
  1708. // }
  1709. // if (lasti >= 0)
  1710. // {
  1711. // /* kill old solvable so that we do not loop */
  1712. // p = solv->branches.elements[lasti];
  1713. // solv->branches.elements[lasti] = 0;
  1714. // POOL_DEBUG(SAT_DEBUG_SOLVER, "minimizing %d -> %d with %s\n", solv->decisionmap[p], lastl, solvid2str(pool, p));
  1715. // minimizationsteps++;
  1716. //
  1717. // level = lastl;
  1718. // revert(solv, level);
  1719. // why = -solv->decisionq_why.elements[solv->decisionq_why.count];
  1720. // assert(why >= 0);
  1721. // olevel = level;
  1722. // level = setpropagatelearn(solv, level, p, disablerules, why);
  1723. // if (level == 0)
  1724. // {
  1725. // queue_free(&dq);
  1726. // queue_free(&dqs);
  1727. // return;
  1728. // }
  1729. // continue; /* back to main loop */
  1730. // }
  1731. // }
  1732. }
  1733. break;
  1734. }
  1735. }
  1736. public function printDecisionMap()
  1737. {
  1738. echo "DecisionMap: \n";
  1739. foreach ($this->decisionMap as $packageId => $level) {
  1740. if ($level > 0) {
  1741. echo ' +' . $this->pool->packageById($packageId) . "\n";
  1742. } else {
  1743. echo ' -' . $this->pool->packageById($packageId) . "\n";
  1744. }
  1745. }
  1746. echo "\n";
  1747. }
  1748. public function printDecisionQueue()
  1749. {
  1750. echo "DecisionQueue: \n";
  1751. foreach ($this->decisionQueue as $i => $literal) {
  1752. echo ' ' . $literal . ' ' . $this->decisionQueueWhy[$i] . "\n";
  1753. }
  1754. echo "\n";
  1755. }
  1756. }