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:
		 Todd Gamblin
					Todd Gamblin
				
			
				
					committed by
					
						 Greg Becker
						Greg Becker
					
				
			
			
				
	
			
			
			 Greg Becker
						Greg Becker
					
				
			
						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,33 +2022,67 @@ 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) |  | ||||||
|     """ |     """ | ||||||
|     driver = PyclingoDriver() |     def __init__(self): | ||||||
|     if "asp" in dump: |         self.set_default_configuration() | ||||||
|         driver.out = sys.stdout |  | ||||||
| 
 | 
 | ||||||
|     # Check upfront that the variants are admissible |     def set_default_configuration(self): | ||||||
|     for root in specs: |         self.reuse = False | ||||||
|         for s in root.traverse(): |         self.dump = () | ||||||
|             if s.virtual: |         self.models = 0 | ||||||
|                 continue |         self.timers = False | ||||||
|             spack.spec.Spec.ensure_valid_variants(s) |         self.stats = False | ||||||
|  |         self.tests = False | ||||||
| 
 | 
 | ||||||
|     setup = SpackSolverSetup() |     def solve(self, specs): | ||||||
|     return driver.solve( |         driver = PyclingoDriver() | ||||||
|         setup, specs, dump, models, timers, stats, tests, reuse |         if "asp" in self.dump: | ||||||
|     ) |             driver.out = sys.stdout | ||||||
|  | 
 | ||||||
|  |         # Check upfront that the variants are admissible | ||||||
|  |         for root in specs: | ||||||
|  |             for s in root.traverse(): | ||||||
|  |                 if s.virtual: | ||||||
|  |                     continue | ||||||
|  |                 spack.spec.Spec.ensure_valid_variants(s) | ||||||
|  | 
 | ||||||
|  |         setup = SpackSolverSetup(reuse=self.reuse, tests=self.tests) | ||||||
|  |         return driver.solve( | ||||||
|  |             setup, specs, self.dump, self.models, self.timers, self.stats | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class UnsatisfiableSpecError(spack.error.UnsatisfiableSpecError): | class UnsatisfiableSpecError(spack.error.UnsatisfiableSpecError): | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user