refactor: convert spack.solver.asp.solve() to a class
The solver has a lot of configuration associated with it. Rather than adding arguments to everything, we should encapsulate that in a class. This is the start of that work; it replaces `solve()` and its kwargs with a class and properties.
This commit is contained in:
parent
87a3b72ef0
commit
1903e45eec
@ -102,11 +102,15 @@ def solve(parser, args):
|
|||||||
|
|
||||||
specs = spack.cmd.parse_specs(args.specs)
|
specs = spack.cmd.parse_specs(args.specs)
|
||||||
|
|
||||||
# dump generated ASP program
|
# set up solver parameters
|
||||||
result = asp.solve(
|
solver = asp.Solver()
|
||||||
specs, dump=dump, models=models, timers=args.timers, stats=args.stats,
|
solver.reuse = args.reuse
|
||||||
reuse=args.reuse,
|
solver.dump = dump
|
||||||
)
|
solver.models = models
|
||||||
|
solver.timers = args.timers
|
||||||
|
solver.stats = args.stats
|
||||||
|
|
||||||
|
result = solver.solve(specs)
|
||||||
if 'solutions' not in dump:
|
if 'solutions' not in dump:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -748,11 +748,12 @@ def concretize_specs_together(*abstract_specs, **kwargs):
|
|||||||
|
|
||||||
def _concretize_specs_together_new(*abstract_specs, **kwargs):
|
def _concretize_specs_together_new(*abstract_specs, **kwargs):
|
||||||
import spack.solver.asp
|
import spack.solver.asp
|
||||||
concretization_kwargs = {
|
|
||||||
'tests': kwargs.get('tests', False),
|
solver = spack.solver.asp.Solver()
|
||||||
'reuse': kwargs.get('reuse', False)
|
solver.tests = kwargs.get('tests', False)
|
||||||
}
|
solver.reuse = kwargs.get('reuse', False)
|
||||||
result = spack.solver.asp.solve(abstract_specs, **concretization_kwargs)
|
|
||||||
|
result = solver.solve(abstract_specs)
|
||||||
result.raise_if_unsat()
|
result.raise_if_unsat()
|
||||||
return [s.copy() for s in result.specs]
|
return [s.copy() for s in result.specs]
|
||||||
|
|
||||||
|
@ -529,7 +529,7 @@ def fact(self, head, assumption=False):
|
|||||||
|
|
||||||
def solve(
|
def solve(
|
||||||
self, solver_setup, specs, dump=None, nmodels=0,
|
self, solver_setup, specs, dump=None, nmodels=0,
|
||||||
timers=False, stats=False, tests=False, reuse=False,
|
timers=False, stats=False,
|
||||||
):
|
):
|
||||||
timer = spack.util.timer.Timer()
|
timer = spack.util.timer.Timer()
|
||||||
|
|
||||||
@ -545,7 +545,7 @@ def solve(
|
|||||||
self.assumptions = []
|
self.assumptions = []
|
||||||
with self.control.backend() as backend:
|
with self.control.backend() as backend:
|
||||||
self.backend = backend
|
self.backend = backend
|
||||||
solver_setup.setup(self, specs, tests=tests, reuse=reuse)
|
solver_setup.setup(self, specs)
|
||||||
timer.phase("setup")
|
timer.phase("setup")
|
||||||
|
|
||||||
# read in the main ASP program and display logic -- these are
|
# read in the main ASP program and display logic -- these are
|
||||||
@ -640,7 +640,7 @@ def stringify(x):
|
|||||||
class SpackSolverSetup(object):
|
class SpackSolverSetup(object):
|
||||||
"""Class to set up and run a Spack concretization solve."""
|
"""Class to set up and run a Spack concretization solve."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, reuse=False, tests=False):
|
||||||
self.gen = None # set by setup()
|
self.gen = None # set by setup()
|
||||||
|
|
||||||
self.declared_versions = {}
|
self.declared_versions = {}
|
||||||
@ -665,6 +665,12 @@ def __init__(self):
|
|||||||
# Caches to optimize the setup phase of the solver
|
# Caches to optimize the setup phase of the solver
|
||||||
self.target_specs_cache = None
|
self.target_specs_cache = None
|
||||||
|
|
||||||
|
# whether to add installed/binary hashes to the solve
|
||||||
|
self.reuse = reuse
|
||||||
|
|
||||||
|
# whether to add installed/binary hashes to the solve
|
||||||
|
self.tests = tests
|
||||||
|
|
||||||
def pkg_version_rules(self, pkg):
|
def pkg_version_rules(self, pkg):
|
||||||
"""Output declared versions of a package.
|
"""Output declared versions of a package.
|
||||||
|
|
||||||
@ -866,7 +872,7 @@ def pkg_rules(self, pkg, tests):
|
|||||||
self.package_provider_rules(pkg)
|
self.package_provider_rules(pkg)
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
self.package_dependencies_rules(pkg, tests)
|
self.package_dependencies_rules(pkg)
|
||||||
|
|
||||||
# virtual preferences
|
# virtual preferences
|
||||||
self.virtual_preferences(
|
self.virtual_preferences(
|
||||||
@ -932,17 +938,17 @@ def package_provider_rules(self, pkg):
|
|||||||
))
|
))
|
||||||
self.gen.newline()
|
self.gen.newline()
|
||||||
|
|
||||||
def package_dependencies_rules(self, pkg, tests):
|
def package_dependencies_rules(self, pkg):
|
||||||
"""Translate 'depends_on' directives into ASP logic."""
|
"""Translate 'depends_on' directives into ASP logic."""
|
||||||
for _, conditions in sorted(pkg.dependencies.items()):
|
for _, conditions in sorted(pkg.dependencies.items()):
|
||||||
for cond, dep in sorted(conditions.items()):
|
for cond, dep in sorted(conditions.items()):
|
||||||
deptypes = dep.type.copy()
|
deptypes = dep.type.copy()
|
||||||
# Skip test dependencies if they're not requested
|
# Skip test dependencies if they're not requested
|
||||||
if not tests:
|
if not self.tests:
|
||||||
deptypes.discard("test")
|
deptypes.discard("test")
|
||||||
|
|
||||||
# ... or if they are requested only for certain packages
|
# ... or if they are requested only for certain packages
|
||||||
if not isinstance(tests, bool) and pkg.name not in tests:
|
if not isinstance(self.tests, bool) and pkg.name not in self.tests:
|
||||||
deptypes.discard("test")
|
deptypes.discard("test")
|
||||||
|
|
||||||
# if there are no dependency types to be considered
|
# if there are no dependency types to be considered
|
||||||
@ -1642,7 +1648,7 @@ def define_installed_packages(self, specs, possible):
|
|||||||
# TODO: (or any mirror really) doesn't have binaries.
|
# TODO: (or any mirror really) doesn't have binaries.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def setup(self, driver, specs, tests=False, reuse=False):
|
def setup(self, driver, specs):
|
||||||
"""Generate an ASP program with relevant constraints for specs.
|
"""Generate an ASP program with relevant constraints for specs.
|
||||||
|
|
||||||
This calls methods on the solve driver to set up the problem with
|
This calls methods on the solve driver to set up the problem with
|
||||||
@ -1689,7 +1695,7 @@ def setup(self, driver, specs, tests=False, reuse=False):
|
|||||||
self.gen.h1("Concrete input spec definitions")
|
self.gen.h1("Concrete input spec definitions")
|
||||||
self.define_concrete_input_specs(specs, possible)
|
self.define_concrete_input_specs(specs, possible)
|
||||||
|
|
||||||
if reuse:
|
if self.reuse:
|
||||||
self.gen.h1("Installed packages")
|
self.gen.h1("Installed packages")
|
||||||
self.gen.fact(fn.optimize_for_reuse())
|
self.gen.fact(fn.optimize_for_reuse())
|
||||||
self.gen.newline()
|
self.gen.newline()
|
||||||
@ -1713,7 +1719,7 @@ def setup(self, driver, specs, tests=False, reuse=False):
|
|||||||
self.gen.h1('Package Constraints')
|
self.gen.h1('Package Constraints')
|
||||||
for pkg in sorted(pkgs):
|
for pkg in sorted(pkgs):
|
||||||
self.gen.h2('Package rules: %s' % pkg)
|
self.gen.h2('Package rules: %s' % pkg)
|
||||||
self.pkg_rules(pkg, tests=tests)
|
self.pkg_rules(pkg, tests=self.tests)
|
||||||
self.gen.h2('Package preferences: %s' % pkg)
|
self.gen.h2('Package preferences: %s' % pkg)
|
||||||
self.preferred_variants(pkg)
|
self.preferred_variants(pkg)
|
||||||
self.preferred_targets(pkg)
|
self.preferred_targets(pkg)
|
||||||
@ -2016,20 +2022,54 @@ def _develop_specs_from_env(spec, env):
|
|||||||
spec.constrain(dev_info['spec'])
|
spec.constrain(dev_info['spec'])
|
||||||
|
|
||||||
|
|
||||||
#
|
class Solver(object):
|
||||||
# These are handwritten parts for the Spack ASP model.
|
"""This is the main external interface class for solving.
|
||||||
#
|
|
||||||
def solve(specs, dump=(), models=0, timers=False, stats=False, tests=False,
|
It manages solver configuration and preferences in once place. It sets up the solve
|
||||||
reuse=False):
|
and passes the setup method to the driver, as well.
|
||||||
"""Solve for a stable model of specs.
|
|
||||||
|
Properties of interest:
|
||||||
|
|
||||||
|
``reuse (bool)``
|
||||||
|
Whether to try to reuse existing installs/binaries
|
||||||
|
|
||||||
|
``show (tuple)``
|
||||||
|
What information to print to the console while running. Options are:
|
||||||
|
* asp: asp program text
|
||||||
|
* opt: optimization criteria for best model
|
||||||
|
* output: raw clingo output
|
||||||
|
* solutions: models found by asp program
|
||||||
|
* all: all of the above
|
||||||
|
|
||||||
|
``models (int)``
|
||||||
|
Number of models to search (default: 0 for unlimited)
|
||||||
|
|
||||||
|
``timers (bool)``
|
||||||
|
Print out coarse fimers for different solve phases.
|
||||||
|
|
||||||
|
``stats (bool)``
|
||||||
|
Print out detailed stats from clingo
|
||||||
|
|
||||||
|
``tests (bool or tuple)``
|
||||||
|
If ``True``, concretize test dependencies for all packages. If
|
||||||
|
a tuple of package names, concretize test dependencies for named
|
||||||
|
packages. If ``False``, do not concretize test dependencies.
|
||||||
|
|
||||||
Arguments:
|
|
||||||
specs (list): list of Specs to solve.
|
|
||||||
dump (tuple): what to dump
|
|
||||||
models (int): number of models to search (default: 0)
|
|
||||||
"""
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.set_default_configuration()
|
||||||
|
|
||||||
|
def set_default_configuration(self):
|
||||||
|
self.reuse = False
|
||||||
|
self.dump = ()
|
||||||
|
self.models = 0
|
||||||
|
self.timers = False
|
||||||
|
self.stats = False
|
||||||
|
self.tests = False
|
||||||
|
|
||||||
|
def solve(self, specs):
|
||||||
driver = PyclingoDriver()
|
driver = PyclingoDriver()
|
||||||
if "asp" in dump:
|
if "asp" in self.dump:
|
||||||
driver.out = sys.stdout
|
driver.out = sys.stdout
|
||||||
|
|
||||||
# Check upfront that the variants are admissible
|
# Check upfront that the variants are admissible
|
||||||
@ -2039,9 +2079,9 @@ def solve(specs, dump=(), models=0, timers=False, stats=False, tests=False,
|
|||||||
continue
|
continue
|
||||||
spack.spec.Spec.ensure_valid_variants(s)
|
spack.spec.Spec.ensure_valid_variants(s)
|
||||||
|
|
||||||
setup = SpackSolverSetup()
|
setup = SpackSolverSetup(reuse=self.reuse, tests=self.tests)
|
||||||
return driver.solve(
|
return driver.solve(
|
||||||
setup, specs, dump, models, timers, stats, tests, reuse
|
setup, specs, self.dump, self.models, self.timers, self.stats
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -2615,7 +2615,11 @@ def _new_concretize(self, tests=False, reuse=False):
|
|||||||
if self._concrete:
|
if self._concrete:
|
||||||
return
|
return
|
||||||
|
|
||||||
result = spack.solver.asp.solve([self], tests=tests, reuse=reuse)
|
solver = spack.solver.asp.Solver()
|
||||||
|
solver.reuse = reuse
|
||||||
|
solver.tests = tests
|
||||||
|
|
||||||
|
result = solver.solve([self])
|
||||||
result.raise_if_unsat()
|
result.raise_if_unsat()
|
||||||
|
|
||||||
# take the best answer
|
# take the best answer
|
||||||
|
Loading…
Reference in New Issue
Block a user