ASP-based solver: avoid cycles in clingo using hidden directive (#40720)
The code should be functonally equivalent to what it was before, but now to avoid cycles by design we are using a "hidden" feature of clingo
This commit is contained in:
parent
2a797f90b4
commit
6983db1392
3 changed files with 4 additions and 50 deletions
|
@ -8,7 +8,6 @@
|
||||||
import enum
|
import enum
|
||||||
import itertools
|
import itertools
|
||||||
import os
|
import os
|
||||||
import pathlib
|
|
||||||
import pprint
|
import pprint
|
||||||
import re
|
import re
|
||||||
import types
|
import types
|
||||||
|
@ -889,14 +888,6 @@ def on_model(model):
|
||||||
|
|
||||||
timer.start("solve")
|
timer.start("solve")
|
||||||
solve_result = self.control.solve(**solve_kwargs)
|
solve_result = self.control.solve(**solve_kwargs)
|
||||||
|
|
||||||
if solve_result.satisfiable and self._model_has_cycles(models):
|
|
||||||
tty.debug(f"cycles detected, falling back to slower algorithm [specs={specs}]")
|
|
||||||
self.control.load(os.path.join(parent_dir, "cycle_detection.lp"))
|
|
||||||
self.control.ground([("no_cycle", [])])
|
|
||||||
models.clear()
|
|
||||||
solve_result = self.control.solve(**solve_kwargs)
|
|
||||||
|
|
||||||
timer.stop("solve")
|
timer.stop("solve")
|
||||||
|
|
||||||
# once done, construct the solve result
|
# once done, construct the solve result
|
||||||
|
@ -950,26 +941,6 @@ def on_model(model):
|
||||||
|
|
||||||
return result, timer, self.control.statistics
|
return result, timer, self.control.statistics
|
||||||
|
|
||||||
def _model_has_cycles(self, models):
|
|
||||||
"""Returns true if the best model has cycles in it"""
|
|
||||||
cycle_detection = clingo.Control()
|
|
||||||
parent_dir = pathlib.Path(__file__).parent
|
|
||||||
lp_file = parent_dir / "cycle_detection.lp"
|
|
||||||
|
|
||||||
min_cost, best_model = min(models)
|
|
||||||
with cycle_detection.backend() as backend:
|
|
||||||
for atom in best_model:
|
|
||||||
if atom.name == "attr" and str(atom.arguments[0]) == '"depends_on"':
|
|
||||||
symbol = fn.depends_on(atom.arguments[1], atom.arguments[2])
|
|
||||||
atom_id = backend.add_atom(symbol.symbol())
|
|
||||||
backend.add_rule([atom_id], [], choice=False)
|
|
||||||
|
|
||||||
cycle_detection.load(str(lp_file))
|
|
||||||
cycle_detection.ground([("base", []), ("no_cycle", [])])
|
|
||||||
cycle_result = cycle_detection.solve()
|
|
||||||
|
|
||||||
return cycle_result.unsatisfiable
|
|
||||||
|
|
||||||
|
|
||||||
class ConcreteSpecsByHash(collections.abc.Mapping):
|
class ConcreteSpecsByHash(collections.abc.Mapping):
|
||||||
"""Mapping containing concrete specs keyed by DAG hash.
|
"""Mapping containing concrete specs keyed by DAG hash.
|
||||||
|
|
|
@ -1325,6 +1325,10 @@ build_priority(PackageNode, 0) :- not build(PackageNode), attr("node", Package
|
||||||
|
|
||||||
#defined installed_hash/2.
|
#defined installed_hash/2.
|
||||||
|
|
||||||
|
% This statement, which is a hidden feature of clingo, let us avoid cycles in the DAG
|
||||||
|
#edge (A, B) : depends_on(A, B).
|
||||||
|
|
||||||
|
|
||||||
%-----------------------------------------------------------------
|
%-----------------------------------------------------------------
|
||||||
% Optimization to avoid errors
|
% Optimization to avoid errors
|
||||||
%-----------------------------------------------------------------
|
%-----------------------------------------------------------------
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
% Copyright 2013-2023 Lawrence Livermore National Security, LLC and other
|
|
||||||
% Spack Project Developers. See the top-level COPYRIGHT file for details.
|
|
||||||
%
|
|
||||||
% SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
|
||||||
|
|
||||||
%=============================================================================
|
|
||||||
% Avoid cycles in the DAG
|
|
||||||
%
|
|
||||||
% Some combinations of conditional dependencies can result in cycles;
|
|
||||||
% this ensures that we solve around them. Note that these rules are quite
|
|
||||||
% demanding on both grounding and solving, since they need to compute and
|
|
||||||
% consider all possible paths between pair of nodes.
|
|
||||||
%=============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
#program no_cycle.
|
|
||||||
path(Parent, Child) :- depends_on(Parent, Child).
|
|
||||||
path(Parent, Descendant) :- path(Parent, A), depends_on(A, Descendant).
|
|
||||||
:- path(A, A).
|
|
||||||
|
|
||||||
#defined depends_on/2.
|
|
Loading…
Reference in a new issue