summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Gamblin <tgamblin@llnl.gov>2021-10-25 01:21:19 -0700
committerTodd Gamblin <tgamblin@llnl.gov>2021-11-05 00:15:47 -0700
commitace4586bf80d8d4dacfbd6ee74d92fb8e6ed5335 (patch)
tree296f703a34806124324fbcf04c3ab13847c36885
parentc537785f6fc9da7ab81e3a6413e839aac4ec7fd3 (diff)
downloadspack-ace4586bf80d8d4dacfbd6ee74d92fb8e6ed5335.tar.gz
spack-ace4586bf80d8d4dacfbd6ee74d92fb8e6ed5335.tar.bz2
spack-ace4586bf80d8d4dacfbd6ee74d92fb8e6ed5335.tar.xz
spack-ace4586bf80d8d4dacfbd6ee74d92fb8e6ed5335.zip
concretizer: rework spack solve output to handle reuse better
-rw-r--r--lib/spack/spack/cmd/solve.py18
-rw-r--r--lib/spack/spack/solver/asp.py77
-rw-r--r--lib/spack/spack/solver/concretize.lp117
3 files changed, 137 insertions, 75 deletions
diff --git a/lib/spack/spack/cmd/solve.py b/lib/spack/spack/cmd/solve.py
index 123c68862d..897eebfa4b 100644
--- a/lib/spack/spack/cmd/solve.py
+++ b/lib/spack/spack/cmd/solve.py
@@ -122,13 +122,21 @@ def solve(parser, args):
tty.msg("Best of %d considered solutions." % result.nmodels)
tty.msg("Optimization Criteria:")
- maxlen = max(len(s) for s in result.criteria)
+ maxlen = max(len(s[2]) for s in result.criteria)
color.cprint(
- "@*{ Priority Criterion %sValue}" % ((maxlen - 10) * " ")
+ "@*{ Priority Criterion %sInstalled ToBuild}" % ((maxlen - 10) * " ")
)
- for i, (name, val) in enumerate(zip(result.criteria, opt)):
- fmt = " @K{%%-8d} %%-%ds%%5d" % maxlen
- color.cprint(fmt % (i + 1, name, val))
+
+ fmt = " @K{%%-8d} %%-%ds%%9s %%7s" % maxlen
+ for i, (idx, build_idx, name) in enumerate(result.criteria, 1):
+ color.cprint(
+ fmt % (
+ i,
+ name,
+ "-" if build_idx is None else opt[idx],
+ opt[idx] if build_idx is None else opt[build_idx],
+ )
+ )
print()
for spec in result.specs:
diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py
index 5fed553080..69f25b39b9 100644
--- a/lib/spack/spack/solver/asp.py
+++ b/lib/spack/spack/solver/asp.py
@@ -2,7 +2,7 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-from __future__ import print_function
+from __future__ import division, print_function
import collections
import copy
@@ -92,6 +92,67 @@ DeclaredVersion = collections.namedtuple(
'DeclaredVersion', ['version', 'idx', 'origin']
)
+# Below numbers are used to map names of criteria to the order
+# they appear in the solution. See concretize.lp
+
+#: Priority offset for "build" criteria (regular criterio shifted to
+#: higher priority for specs we have to build)
+build_priority_offset = 200
+
+#: Priority offset of "fixed" criteria (those w/o build criteria)
+fixed_priority_offset = 100
+
+
+def build_criteria_names(costs, tuples):
+ """Construct an ordered mapping from criteria names to indices in the cost list."""
+ # pull optimization criteria names out of the solution
+ priorities_names = []
+
+ num_fixed = 0
+ for pred, args in tuples:
+ if pred != "opt_criterion":
+ continue
+
+ priority, name = args[:2]
+ priority = int(priority)
+
+ # add the priority of this opt criterion and its name
+ priorities_names.append((priority, name))
+
+ # if the priority is less than fixed_priority_offset, then it
+ # has an associated build priority -- the same criterion but for
+ # nodes that we have to build.
+ if priority < fixed_priority_offset:
+ build_priority = priority + build_priority_offset
+ priorities_names.append((build_priority, name))
+ else:
+ num_fixed += 1
+
+ # sort the criteria by priority
+ priorities_names = sorted(priorities_names, reverse=True)
+
+ assert len(priorities_names) == len(costs), "Wrong number of optimization criteria!"
+
+ # split list into three parts: build criteria, fixed criteria, non-build criteria
+ num_criteria = len(priorities_names)
+ num_build = (num_criteria - num_fixed) // 2
+
+ build = priorities_names[:num_build]
+ fixed = priorities_names[num_build:num_build + num_fixed]
+ installed = priorities_names[num_build + num_fixed:]
+
+ # mapping from priority to index in cost list
+ indices = dict((p, i) for i, (p, n) in enumerate(priorities_names))
+
+ # make a list that has each name with its build and non-build priority
+ criteria = [
+ (p - fixed_priority_offset + num_build, None, name) for p, name in fixed
+ ]
+ for (i, name), (b, _) in zip(installed, build):
+ criteria.append((indices[i], indices[b], name))
+
+ return criteria
+
def issequence(obj):
if isinstance(obj, string_types):
@@ -531,13 +592,7 @@ class PyclingoDriver(object):
# add best spec to the results
result.answers.append((list(min_cost), 0, answers))
-
- # pull optimization criteria names out of the solution
- criteria = [
- (int(args[0]), args[1]) for name, args in tuples
- if name == "opt_criterion"
- ]
- result.criteria = [t[1] for t in sorted(criteria, reverse=True)]
+ result.criteria = build_criteria_names(min_cost, tuples)
# record the number of models the solver considered
result.nmodels = len(models)
@@ -1635,6 +1690,9 @@ class SpackSolverSetup(object):
class SpecBuilder(object):
"""Class with actions to rebuild a spec from ASP results."""
+ #: Attributes that don't need actions
+ ignored_attributes = ["opt_criterion"]
+
def __init__(self, specs):
self._result = None
self._command_line_specs = specs
@@ -1797,6 +1855,9 @@ class SpecBuilder(object):
self._specs = {}
for name, args in function_tuples:
+ if name in SpecBuilder.ignored_attributes:
+ continue
+
action = getattr(self, name, None)
# print out unknown actions so we can display them for debugging
diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp
index cab54b9bd0..430f1c81c8 100644
--- a/lib/spack/spack/solver/concretize.lp
+++ b/lib/spack/spack/solver/concretize.lp
@@ -793,7 +793,12 @@ build(Package) :- not hash(Package, _), node(Package).
% defaults and preferences. This is implemented by bumping the priority of optimization
% criteria for built specs -- so that they take precedence over the otherwise
% topmost-priority criterion to reuse what is installed.
-build_priority(Package, 100) :- build(Package), node(Package).
+%
+% The priority ranges are:
+% 200+ Shifted priorities for build nodes; correspond to priorities 0 - 99.
+% 100 - 199 Unshifted priorities. Currently only includes minimizing #builds.
+% 0 - 99 Priorities for non-built nodes.
+build_priority(Package, 200) :- build(Package), node(Package).
build_priority(Package, 0) :- not build(Package), node(Package).
#defined installed_hash/2.
@@ -807,18 +812,17 @@ build_priority(Package, 0) :- not build(Package), node(Package).
% is displayed (clingo doesn't display sums over empty sets by default)
% Try hard to reuse installed packages (i.e., minimize the number built)
-opt_criterion(17, "number of packages to build (vs. reuse)").
-#minimize { 0@17: #true }.
-#minimize { 1@17,Package : build(Package), optimize_for_reuse() }.
+opt_criterion(100, "number of packages to build (vs. reuse)").
+#minimize { 0@100: #true }.
+#minimize { 1@100,Package : build(Package), optimize_for_reuse() }.
#defined optimize_for_reuse/0.
% Minimize the number of deprecated versions being used
-opt_criterion(116, "(build) deprecated versions used").
-opt_criterion(16, "deprecated versions used").
-#minimize{ 0@116: #true }.
-#minimize{ 0@16: #true }.
+opt_criterion(14, "deprecated versions used").
+#minimize{ 0@214: #true }.
+#minimize{ 0@14: #true }.
#minimize{
- 1@16+Priority,Package
+ 1@14+Priority,Package
: deprecated(Package, _),
build_priority(Package, Priority)
}.
@@ -827,33 +831,30 @@ opt_criterion(16, "deprecated versions used").
% 1. Version weight
% 2. Number of variants with a non default value, if not set
% for the root(Package)
-opt_criterion(115, "(build) version weight").
-opt_criterion(15, "version weight").
-#minimize{ 0@115: #true }.
-#minimize{ 0@15: #true }.
+opt_criterion(13, "version weight").
+#minimize{ 0@213: #true }.
+#minimize{ 0@13: #true }.
#minimize {
- Weight@15+Priority
+ Weight@13+Priority
: root(Package),version_weight(Package, Weight),
build_priority(Package, Priority)
}.
-opt_criterion(114, "(build) number of non-default variants (roots)").
-opt_criterion(14, "number of non-default variants (roots)").
-#minimize{ 0@114: #true }.
-#minimize{ 0@14: #true }.
+opt_criterion(12, "number of non-default variants (roots)").
+#minimize{ 0@212: #true }.
+#minimize{ 0@12: #true }.
#minimize {
- Weight@14+Priority,Package,Variant,Value
+ Weight@12+Priority,Package,Variant,Value
: variant_not_default(Package, Variant, Value, Weight),
root(Package),
build_priority(Package, Priority)
}.
-opt_criterion(112, "(build) preferred providers for roots").
-opt_criterion(12, "preferred providers for roots").
-#minimize{ 0@112 : #true }.
-#minimize{ 0@12: #true }.
+opt_criterion(11, "preferred providers for roots").
+#minimize{ 0@211 : #true }.
+#minimize{ 0@11: #true }.
#minimize{
- Weight@12+Priority,Provider,Virtual
+ Weight@11+Priority,Provider,Virtual
: provider_weight(Provider, Virtual, Weight),
root(Provider),
build_priority(Provider, Priority)
@@ -862,12 +863,11 @@ opt_criterion(12, "preferred providers for roots").
% If the value is a multivalued variant there could be multiple
% values set as default. Since a default value has a weight of 0 we
% need to maximize their number below to ensure they're all set
-opt_criterion(111, "(build) number of values in multi-valued variants (root)").
-opt_criterion(11, "number of values in multi-valued variants (root)").
-#minimize{ 0@111 : #true }.
-#minimize{ 0@11 : #true }.
+opt_criterion(10, "number of values in multi-valued variants (root)").
+#minimize{ 0@210 : #true }.
+#minimize{ 0@10 : #true }.
#maximize {
- 1@11+Priority,Package,Variant,Value
+ 1@10+Priority,Package,Variant,Value
: variant_not_default(Package, Variant, Value, Weight),
not variant_single_value(Package, Variant),
root(Package),
@@ -875,11 +875,11 @@ opt_criterion(11, "number of values in multi-valued variants (root)").
}.
% Try to use default variants or variants that have been set
-opt_criterion(110, "(build) number of non-default variants (non-roots)").
-opt_criterion(10, "number of non-default variants (non-roots)").
-#minimize{ 0@10: #true }.
+opt_criterion(9, "number of non-default variants (non-roots)").
+#minimize{ 0@209: #true }.
+#minimize{ 0@9: #true }.
#minimize {
- Weight@10+Priority,Package,Variant,Value
+ Weight@9+Priority,Package,Variant,Value
: variant_not_default(Package, Variant, Value, Weight),
not root(Package),
build_priority(Package, Priority)
@@ -887,62 +887,57 @@ opt_criterion(10, "number of non-default variants (non-roots)").
% Minimize the weights of the providers, i.e. use as much as
% possible the most preferred providers
-opt_criterion(109, "(build) preferred providers (non-roots)").
-opt_criterion(9, "preferred providers (non-roots)").
-#minimize{ 0@109: #true }.
-#minimize{ 0@9: #true }.
+opt_criterion(8, "preferred providers (non-roots)").
+#minimize{ 0@208: #true }.
+#minimize{ 0@8: #true }.
#minimize{
- Weight@9+Priority,Provider,Virtual
+ Weight@8+Priority,Provider,Virtual
: provider_weight(Provider, Virtual, Weight), not root(Provider),
build_priority(Provider, Priority)
}.
% Try to minimize the number of compiler mismatches in the DAG.
-opt_criterion(108, "(build) compiler mismatches").
-opt_criterion(8, "compiler mismatches").
-#minimize{ 0@108: #true }.
-#minimize{ 0@8: #true }.
+opt_criterion(7, "compiler mismatches").
+#minimize{ 0@207: #true }.
+#minimize{ 0@7: #true }.
#minimize{
- 1@8+Priority,Package,Dependency
+ 1@7+Priority,Package,Dependency
: compiler_mismatch(Package, Dependency),
build_priority(Package, Priority)
}.
% Try to minimize the number of compiler mismatches in the DAG.
-opt_criterion(107, "(build) OS mismatches").
-opt_criterion(7, "OS mismatches").
-#minimize{ 0@107: #true }.
-#minimize{ 0@7: #true }.
+opt_criterion(6, "OS mismatches").
+#minimize{ 0@206: #true }.
+#minimize{ 0@6: #true }.
#minimize{
- 1@7+Priority,Package,Dependency
+ 1@6+Priority,Package,Dependency
: node_os_mismatch(Package, Dependency),
build_priority(Package, Priority)
}.
-opt_criterion(106, "(build) non-preferred OSes").
-#minimize{ 0@106: #true }.
-#minimize{ 0@6: #true }.
+opt_criterion(5, "non-preferred OS's").
+#minimize{ 0@205: #true }.
+#minimize{ 0@5: #true }.
#minimize{
- Weight@6+Priority,Package
+ Weight@5+Priority,Package
: node_os_weight(Package, Weight),
build_priority(Package, Priority)
}.
% Choose more recent versions for nodes
-opt_criterion(105, "(build) version badness").
opt_criterion(5, "version badness").
-#minimize{ 0@105: #true }.
-#minimize{ 0@5: #true }.
+#minimize{ 0@204: #true }.
+#minimize{ 0@4: #true }.
#minimize{
- Weight@5+Priority,Package
+ Weight@4+Priority,Package
: version_weight(Package, Weight),
build_priority(Package, Priority)
}.
% Try to use preferred compilers
-opt_criterion(103, "(build) non-preferred compilers").
opt_criterion(3, "non-preferred compilers").
-#minimize{ 0@103: #true }.
+#minimize{ 0@203: #true }.
#minimize{ 0@3: #true }.
#minimize{
Weight@3+Priority,Package
@@ -952,9 +947,8 @@ opt_criterion(3, "non-preferred compilers").
% Minimize the number of mismatches for targets in the DAG, try
% to select the preferred target.
-opt_criterion(102, "(build) target mismatches").
opt_criterion(2, "target mismatches").
-#minimize{ 0@102: #true }.
+#minimize{ 0@202: #true }.
#minimize{ 0@2: #true }.
#minimize{
1@2+Priority,Package,Dependency
@@ -962,9 +956,8 @@ opt_criterion(2, "target mismatches").
build_priority(Package, Priority)
}.
-opt_criterion(101, "(build) non-preferred targets").
opt_criterion(1, "non-preferred targets").
-#minimize{ 0@101: #true }.
+#minimize{ 0@201: #true }.
#minimize{ 0@1: #true }.
#minimize{
Weight@1+Priority,Package