summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/spack/cmd/solve.py1
-rw-r--r--lib/spack/spack/solver/asp.py88
-rw-r--r--lib/spack/spack/solver/concretize.lp12
-rw-r--r--lib/spack/spack/spec.py1
4 files changed, 60 insertions, 42 deletions
diff --git a/lib/spack/spack/cmd/solve.py b/lib/spack/spack/cmd/solve.py
index 480ced6a29..b343861b55 100644
--- a/lib/spack/spack/cmd/solve.py
+++ b/lib/spack/spack/cmd/solve.py
@@ -105,6 +105,7 @@ def solve(parser, args):
# die if no solution was found
# TODO: we need to be able to provide better error messages than this
if not result.satisfiable:
+ result.print_cores()
tty.die("Unsatisfiable spec.")
# dump the solutions as concretized specs
diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py
index eb01a4b77a..6fb838109e 100644
--- a/lib/spack/spack/solver/asp.py
+++ b/lib/spack/spack/solver/asp.py
@@ -218,6 +218,12 @@ class Result(object):
self.answers = []
self.cores = []
+ def print_cores(self):
+ for core in self.cores:
+ tty.msg(
+ "The following constraints are unsatisfiable:",
+ *sorted(str(symbol) for symbol in core))
+
class ClingoDriver(object):
def __init__(self):
@@ -477,14 +483,30 @@ class PyclingoDriver(object):
[atoms[s] for s in args] + rule_atoms
)
+ def one_of_iff(self, head, versions):
+ self.out.write("%s :- %s.\n" % (head, AspOneOf(*versions)))
+ self.out.write("%s :- %s.\n" % (AspOneOf(*versions), head))
+
+ at_least_1_sym = fn.at_least_1(*head.args).symbol()
+ at_least_1 = self.backend.add_atom(at_least_1_sym)
+
+ more_than_1_sym = fn.more_than_1(*head.args).symbol()
+ more_than_1 = self.backend.add_atom(more_than_1_sym)
+
+ version_atoms = [self.backend.add_atom(f.symbol()) for f in versions]
+ self.backend.add_weight_rule(
+ [at_least_1], 1, [(v, 1) for v in version_atoms])
+ self.backend.add_weight_rule(
+ [more_than_1], 2, [(v, 1) for v in version_atoms])
+
+ head_atom = self.backend.add_atom(head.symbol())
+ self.backend.add_rule([head_atom], [at_least_1, -more_than_1])
+
+ self.backend.add_rule([], [head_atom, more_than_1])
+ self.backend.add_rule([], [head_atom, -at_least_1])
+
def solve(self, solver_setup, specs, dump=None, nmodels=0,
timers=False, stats=False):
- calls = [0]
- class Context(object):
- def version_satisfies(self, a, b):
- calls[0] += 1
- return bool(ver(a.string).satisfies(ver(b.string)))
-
timer = Timer()
# Initialize the control object for the solver
@@ -509,13 +531,14 @@ class PyclingoDriver(object):
# Grounding is the first step in the solve -- it turns our facts
# and first-order logic rules into propositional logic.
- self.control.ground([("base", [])], context=Context())
+ self.control.ground([("base", [])])
timer.phase("ground")
# With a grounded program, we can run the solve.
result = Result()
models = [] # stable moodels if things go well
cores = [] # unsatisfiable cores if they do not
+
def on_model(model):
models.append((model.cost, model.symbols(shown=True)))
@@ -549,6 +572,7 @@ class PyclingoDriver(object):
sym = symbols[atom]
if sym.name == "rule":
sym = sym.arguments[0].string
+ core_symbols.append(sym)
result.cores.append(core_symbols)
if timers:
@@ -636,23 +660,6 @@ class SpackSolverSetup(object):
self.version_constraints.add((spec.name, spec.versions))
return [fn.version_satisfies(spec.name, spec.versions)]
- # # version must be *one* of the ones the spec allows.
- # allowed_versions = [
- # v for v in sorted(self.possible_versions[spec.name])
- # if v.satisfies(spec.versions)
- # ]
-
- # # don't bother restricting anything if all versions are allowed
- # if len(allowed_versions) == len(self.possible_versions[spec.name]):
- # return []
-
- # predicates = [fn.version(spec.name, v) for v in allowed_versions]
-
- # # conflict with any versions that do not satisfy the spec
- # if predicates:
- # return [self.gen.one_of(*predicates)]
- # return []
-
def available_compilers(self):
"""Facts about available compilers."""
@@ -909,7 +916,6 @@ class SpackSolverSetup(object):
if compiler.satisfies(spec.compiler)
]
-
clauses.append(self.gen.one_of(*possible_compiler_versions))
for version in possible_compiler_versions:
clauses.append(
@@ -984,8 +990,8 @@ class SpackSolverSetup(object):
possible.add(spec.architecture.os)
# make directives for possible OS's
- for os in sorted(possible):
- self.gen.fact(fn.os(os))
+ for possible_os in sorted(possible):
+ self.gen.fact(fn.os(possible_os))
# mark this one as default
self.gen.fact(fn.node_os_default(platform.default_os))
@@ -1086,6 +1092,29 @@ class SpackSolverSetup(object):
return cspecs
+ def define_version_constraints(self):
+ """Define what version_satisfies(...) means in ASP logic."""
+ for pkg_name, versions in sorted(self.version_constraints):
+ # version must be *one* of the ones the spec allows.
+ allowed_versions = [
+ v for v in sorted(self.possible_versions[pkg_name])
+ if v.satisfies(versions)
+ ]
+
+ # don't bother restricting anything if all versions are allowed
+ if len(allowed_versions) == len(self.possible_versions[pkg_name]):
+ continue
+
+ predicates = [fn.version(pkg_name, v) for v in allowed_versions]
+
+ # version_satisfies(pkg, constraint) is true if and only if a
+ # satisfying version is set for the package
+ self.gen.one_of_iff(
+ fn.version_satisfies(pkg_name, versions),
+ predicates,
+ )
+ self.gen.newline()
+
def setup(self, driver, specs):
"""Generate an ASP program with relevant constraints for specs.
@@ -1150,9 +1179,8 @@ class SpackSolverSetup(object):
self.gen.fact(clause)
self.gen.h1("Version Constraints")
- for name, versions in sorted(self.version_constraints):
- self.gen.fact(fn.version_constraint(name, versions))
- self.gen.newline()
+ self.define_version_constraints()
+
class SpecBuilder(object):
"""Class with actions to rebuild a spec from ASP results."""
diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp
index 604720c6a4..513f9e3e4a 100644
--- a/lib/spack/spack/solver/concretize.lp
+++ b/lib/spack/spack/solver/concretize.lp
@@ -14,22 +14,10 @@ version_declared(Package, Version) :- version_declared(Package, Version, _).
1 { version(Package, Version) : version_declared(Package, Version) } 1
:- node(Package).
-% no conflicting versions can be assigned
-:- version(Package, Version),
- version_satisfies(Package, Constraint),
- 0 = @version_satisfies(Version, Constraint).
-
-version_satisfies(Package, Constraint)
- :- node(Package),
- version(Package, Version),
- version_constraint(Package, Constraint),
- 1 = @version_satisfies(Version, Constraint).
-
version_weight(Package, Weight)
:- version(Package, Version), version_declared(Package, Version, Weight).
#defined version_conflict/2.
-#defined version_constraint/2.
%-----------------------------------------------------------------------------
% Dependency semantics
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index ab429b5e49..bddba98d71 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -2428,6 +2428,7 @@ class Spec(object):
result = spack.solver.asp.solve([self])
if not result.satisfiable:
+ result.print_cores()
raise spack.error.UnsatisfiableSpecError(
self, "unknown", "Unsatisfiable!")