Parametrize all the logic program for multiple nodes

Introduce the concept of "condition sets", i.e. the set of packages on which
a package can require / impose conditions. This currently maps to the link/run
sub-dag of each package + its direct build dependencies.

Parametrize the "condition" and "requirement" logic to multiple nodes.
This commit is contained in:
Massimiliano Culpo 2023-06-19 20:23:59 +02:00 committed by Todd Gamblin
parent c050b99a06
commit 22c95923e3
3 changed files with 184 additions and 76 deletions

View file

@ -776,6 +776,7 @@ def visit(node):
# Load the file itself # Load the file itself
self.control.load(os.path.join(parent_dir, "concretize.lp")) self.control.load(os.path.join(parent_dir, "concretize.lp"))
self.control.load(os.path.join(parent_dir, "heuristic.lp"))
self.control.load(os.path.join(parent_dir, "os_compatibility.lp")) self.control.load(os.path.join(parent_dir, "os_compatibility.lp"))
self.control.load(os.path.join(parent_dir, "display.lp")) self.control.load(os.path.join(parent_dir, "display.lp"))
timer.stop("load") timer.stop("load")
@ -1284,9 +1285,13 @@ def impose(self, condition_id, imposed_spec, node=True, name=None, body=False):
def package_provider_rules(self, pkg): def package_provider_rules(self, pkg):
for provider_name in sorted(set(s.name for s in pkg.provided.keys())): for provider_name in sorted(set(s.name for s in pkg.provided.keys())):
if provider_name not in self.possible_virtuals:
continue
self.gen.fact(fn.facts(pkg.name, fn.possible_provider(provider_name))) self.gen.fact(fn.facts(pkg.name, fn.possible_provider(provider_name)))
for provided, whens in pkg.provided.items(): for provided, whens in pkg.provided.items():
if provided.name not in self.possible_virtuals:
continue
for when in whens: for when in whens:
msg = "%s provides %s when %s" % (pkg.name, provided, when) msg = "%s provides %s when %s" % (pkg.name, provided, when)
condition_id = self.condition(when, provided, pkg.name, msg) condition_id = self.condition(when, provided, pkg.name, msg)

View file

@ -13,6 +13,9 @@
#const root_node_id = 0. #const root_node_id = 0.
#const link_run = 0.
#const direct_build = 1.
% Allow clingo to create nodes % Allow clingo to create nodes
{ attr("node", node(0..X-1, Package)) } :- max_nodes(Package, X), not virtual(Package). { attr("node", node(0..X-1, Package)) } :- max_nodes(Package, X), not virtual(Package).
@ -39,12 +42,42 @@ max_nodes(Package, 1) :- virtual(Package).
:- attr("node_flag_source", ParentNode, _, _), not attr("node", ParentNode). :- attr("node_flag_source", ParentNode, _, _), not attr("node", ParentNode).
:- attr("node_flag_source", _, _, ChildNode), not attr("node", ChildNode). :- attr("node_flag_source", _, _, ChildNode), not attr("node", ChildNode).
:- attr("virtual_node", VirtualNode), not provider(_, VirtualNode).
:- provider(_, VirtualNode), not attr("virtual_node", VirtualNode).
:- provider(PackageNode, _), not attr("node", PackageNode).
:- attr("root", node(ID, PackageNode)), ID> root_node_id.
% Root nodes cannot depend on non-root nodes if the dependency is "link" or "run"
:- attr("depends_on", node(root_node_id, _), node(ID, _), "link"), ID != root_node_id.
:- attr("depends_on", node(root_node_id, _), node(ID, _), "run"), ID != root_node_id.
% Rules on "unification sets", i.e. on sets of nodes allowing a single configuration of any given package % Rules on "unification sets", i.e. on sets of nodes allowing a single configuration of any given package
unification_set("root", PackageNode) :- attr("root", PackageNode).
unification_set(SetID, ChildNode) :- attr("depends_on", ParentNode, ChildNode, _), unification_set(SetID, ParentNode).
unify(SetID, PackageName) :- unification_set(SetID, node(_, PackageName)). unify(SetID, PackageName) :- unification_set(SetID, node(_, PackageName)).
:- 2 { unification_set(SetID, node(_, PackageName)) }, unify(SetID, PackageName). :- 2 { unification_set(SetID, node(_, PackageName)) }, unify(SetID, PackageName).
unification_set("root", PackageNode) :- attr("root", PackageNode).
unification_set(SetID, ChildNode) :- attr("depends_on", ParentNode, ChildNode, Type), Type != "build", unification_set(SetID, ParentNode).
unification_set(("build", ParentNode), ChildNode) :- attr("depends_on", ParentNode, ChildNode, Type), Type == "build", unification_set("root", ParentNode).
unification_set(SetID, ChildNode) :- attr("depends_on", ParentNode, ChildNode, Type), Type == "build", SetID != "root", unification_set(SetID, ParentNode).
unification_set(SetID, VirtualNode) :- provider(PackageNode, VirtualNode), unification_set(SetID, PackageNode).
%----
% Rules to break symmetry and speed-up searches
%----
% In the "root" unification set only ID = 0 are allowed
:- unification_set("root", node(ID, _)), ID != 0.
% Cannot have a node with an ID, if lower ID of the same package are not used
:- attr("node", node(ID1, Package)),
not attr("node", node(ID2, Package)),
max_nodes(Package, X), ID1=0..X-1, ID2=0..X-1, ID2 < ID1.
:- attr("virtual_node", node(ID1, Package)),
not attr("virtual_node", node(ID2, Package)),
max_nodes(Package, X), ID1=0..X-1, ID2=0..X-1, ID2 < ID1.
% Give clingo the choice to solve an input spec or not % Give clingo the choice to solve an input spec or not
{ literal_solved(ID) } :- literal(ID). { literal_solved(ID) } :- literal(ID).
literal_not_solved(ID) :- not literal_solved(ID), literal(ID). literal_not_solved(ID) :- not literal_solved(ID), literal(ID).
@ -63,15 +96,16 @@ opt_criterion(300, "number of input specs not concretized").
% TODO: literals, at the moment, can only influence the "root" unification set. This needs to be extended later. % TODO: literals, at the moment, can only influence the "root" unification set. This needs to be extended later.
special_case("node_flag_source").
special_case("depends_on").
% Map constraint on the literal ID to facts on the node % Map constraint on the literal ID to facts on the node
attr(Name, node(root_node_id, A1)) :- literal(LiteralID, Name, A1), literal_solved(LiteralID). attr(Name, node(root_node_id, A1)) :- literal(LiteralID, Name, A1), literal_solved(LiteralID).
attr(Name, node(root_node_id, A1), A2) :- literal(LiteralID, Name, A1, A2), literal_solved(LiteralID). attr(Name, node(root_node_id, A1), A2) :- literal(LiteralID, Name, A1, A2), literal_solved(LiteralID).
attr(Name, node(root_node_id, A1), A2, A3) :- literal(LiteralID, Name, A1, A2, A3), literal_solved(LiteralID), not literal_special_case(LiteralID, Name, A1, A2, A3). attr(Name, node(root_node_id, A1), A2, A3) :- literal(LiteralID, Name, A1, A2, A3), literal_solved(LiteralID), not special_case(Name).
attr(Name, node(root_node_id, A1), A2, A3, A4) :- literal(LiteralID, Name, A1, A2, A3, A4), literal_solved(LiteralID). attr(Name, node(root_node_id, A1), A2, A3, A4) :- literal(LiteralID, Name, A1, A2, A3, A4), literal_solved(LiteralID).
% Special cases where nodes occur in arguments other than A1 % Special cases where nodes occur in arguments other than A1
literal_special_case(LiteralID, Name, A1, A2, A3) :- literal(LiteralID, Name, A1, A2, A3), Name == "node_flag_source".
literal_special_case(LiteralID, Name, A1, A2, A3) :- literal(LiteralID, Name, A1, A2, A3), Name == "depends_on".
attr("node_flag_source", node(root_node_id, A1), A2, node(root_node_id, A3)) :- literal(LiteralID, "node_flag_source", A1, A2, A3), literal_solved(LiteralID). attr("node_flag_source", node(root_node_id, A1), A2, node(root_node_id, A3)) :- literal(LiteralID, "node_flag_source", A1, A2, A3), literal_solved(LiteralID).
attr("depends_on", node(root_node_id, A1), node(root_node_id, A2), A3) :- literal(LiteralID, "depends_on", A1, A2, A3), literal_solved(LiteralID). attr("depends_on", node(root_node_id, A1), node(root_node_id, A2), A3) :- literal(LiteralID, "depends_on", A1, A2, A3), literal_solved(LiteralID).
@ -213,30 +247,100 @@ attr("node_version_satisfies", node(ID, Package), Constraint)
%----------------------------------------------------------------------------- %-----------------------------------------------------------------------------
% conditions are specified with `condition_requirement` and hold when % conditions are specified with `condition_requirement` and hold when
% corresponding spec attributes hold. % corresponding spec attributes hold.
condition_holds(ID, node(root_node_id, Package)) :-
facts(Package, condition(ID)); % A "condition_set(PackageNode, _)" is the set of nodes on which PackageNode can require / impose conditions
attr(Name, node(root_node_id, A1)) : condition_requirement(ID, Name, A1); % Currently, for a given node, this is the link-run sub-DAG of PackageNode and its direct build dependencies
attr(Name, node(root_node_id, A1), A2) : condition_requirement(ID, Name, A1, A2); condition_set(PackageNode, PackageNode, link_run) :- attr("node", PackageNode).
attr(Name, node(root_node_id, A1), A2, A3) : condition_requirement(ID, Name, A1, A2, A3), Name != "node_flag_source";
attr(Name, node(root_node_id, A1), A2, A3, A4) : condition_requirement(ID, Name, A1, A2, A3, A4); condition_set(PackageNode, PackageNode, link_run) :- provider(PackageNode, VirtualNode).
condition_set(PackageNode, VirtualNode, link_run) :- provider(PackageNode, VirtualNode).
:- condition_set(node(root_node_id, Package), node(ID, A1), link_run), ID > root_node_id.
condition_set(ID, DependencyNode, link_run)
:- condition_set(ID, PackageNode, link_run),
attr("depends_on", PackageNode, DependencyNode, Type),
Type != "build".
condition_set(PackageNode, DependencyNode, direct_build) :- condition_set(PackageNode, PackageNode, link_run), attr("depends_on", PackageNode, DependencyNode, "build").
condition_set(ID, VirtualNode, Type) :- condition_set(ID, PackageNode, Type), provider(PackageNode, VirtualNode).
condition_set(ID, PackageNode) :- condition_set(ID, PackageNode, _).
condition_packages(ID, A1) :- condition_requirement(ID, _, A1).
condition_packages(ID, A1) :- condition_requirement(ID, _, A1, _).
condition_packages(ID, A1) :- condition_requirement(ID, _, A1, _, _).
condition_packages(ID, A1) :- condition_requirement(ID, _, A1, _, _, _).
node_condition(ID, node(PackageID, Package)) :- facts(Package, condition(ID)), attr("node", node(PackageID, Package)).
node_condition(ID, node(PackageID, Package)) :- facts(Virtual, condition(ID)), provider(node(PackageID, Package), node(_, Virtual)).
condition_nodes(ConditionID, PackageNode, node(X, A1))
:- condition_packages(ConditionID, A1),
condition_set(PackageNode, node(X, A1)),
node_condition(ConditionID, PackageNode).
cannot_hold(ConditionID, PackageNode)
:- condition_packages(ConditionID, A1),
not condition_set(PackageNode, node(_, A1), _),
node_condition(ConditionID, PackageNode).
condition_holds(ID, PackageNode) :-
node_condition(ID, PackageNode);
attr(Name, node(X, A1)) : condition_requirement(ID, Name, A1), condition_nodes(ID, PackageNode, node(X, A1));
attr(Name, node(X, A1), A2) : condition_requirement(ID, Name, A1, A2), condition_nodes(ID, PackageNode, node(X, A1));
attr(Name, node(X, A1), A2, A3) : condition_requirement(ID, Name, A1, A2, A3), condition_nodes(ID, PackageNode, node(X, A1)), not special_case(Name);
attr(Name, node(X, A1), A2, A3, A4) : condition_requirement(ID, Name, A1, A2, A3, A4), condition_nodes(ID, PackageNode, node(X, A1));
% Special cases % Special cases
attr("node_flag_source", node(root_node_id, Package), A2, node(root_node_id, A3)) : condition_requirement(ID, "node_flag_source", Package, A2, A3). attr("node_flag_source", node(X, A1), A2, node(Y, A3)) : condition_requirement(ID, "node_flag_source", A1, A2, A3), condition_nodes(ID, PackageNode, node(X, A1)), condition_nodes(ID, PackageNode, node(Y, A3));
not cannot_hold(ID, PackageNode).
condition_holds(ID, node(VirtualID, Virtual))
:- condition_holds(ID, PackageNode),
facts(Virtual, condition(ID)),
provider(PackageNode, node(VirtualID, Virtual)).
% condition_holds(ID, node(ID, Package)) implies all imposed_constraints, unless do_not_impose(ID, node(ID, Package)) % condition_holds(ID, node(ID, Package)) implies all imposed_constraints, unless do_not_impose(ID, node(ID, Package))
% is derived. This allows imposed constraints to be canceled in special cases. % is derived. This allows imposed constraints to be canceled in special cases.
impose(ID, PackageNode) :- condition_holds(ID, PackageNode), not do_not_impose(ID, PackageNode). impose(ID, PackageNode) :- condition_holds(ID, PackageNode), node_condition(ID, PackageNode), not do_not_impose(ID, PackageNode).
imposed_packages(ID, A1) :- imposed_constraint(ID, _, A1).
imposed_packages(ID, A1) :- imposed_constraint(ID, _, A1, _).
imposed_packages(ID, A1) :- imposed_constraint(ID, _, A1, _, _).
imposed_packages(ID, A1) :- imposed_constraint(ID, _, A1, _, _, _).
imposed_packages(ID, A1) :- imposed_constraint(ID, "depends_on", _, A1, _).
imposed_nodes(ConditionID, PackageNode, node(X, A1))
:- imposed_packages(ConditionID, A1),
condition_set(PackageNode, node(X, A1), _),
node_condition(ConditionID, PackageNode).
imposed_nodes(ConditionID, PackageNode, node(X, A1))
:- imposed_packages(ConditionID, A1),
condition_set(PackageNode, node(X, A1), _),
attr("hash", PackageNode, ConditionID).
:- imposed_packages(ID, A1), impose(ID, PackageNode), not condition_set(PackageNode, node(_, A1)).
% Conditions that hold impose may impose constraints on other specs % Conditions that hold impose may impose constraints on other specs
attr(Name, node(root_node_id, A1)) :- impose(ID, node(NodeID, Package)), imposed_constraint(ID, Name, A1). attr(Name, node(X, A1)) :- impose(ID, PackageNode), imposed_constraint(ID, Name, A1), imposed_nodes(ID, PackageNode, node(X, A1)).
attr(Name, node(root_node_id, A1), A2) :- impose(ID, node(NodeID, Package)), imposed_constraint(ID, Name, A1, A2). attr(Name, node(X, A1), A2) :- impose(ID, PackageNode), imposed_constraint(ID, Name, A1, A2), imposed_nodes(ID, PackageNode, node(X, A1)).
attr(Name, node(root_node_id, A1), A2, A3) :- impose(ID, node(NodeID, Package)), imposed_constraint(ID, Name, A1, A2, A3), not special_case(ID, Name, A1, A2, A3). attr(Name, node(X, A1), A2, A3) :- impose(ID, PackageNode), imposed_constraint(ID, Name, A1, A2, A3), imposed_nodes(ID, PackageNode, node(X, A1)), not special_case(Name).
attr(Name, node(root_node_id, A1), A2, A3, A4) :- impose(ID, node(NodeID, Package)), imposed_constraint(ID, Name, A1, A2, A3, A4). attr(Name, node(X, A1), A2, A3, A4) :- impose(ID, PackageNode), imposed_constraint(ID, Name, A1, A2, A3, A4), imposed_nodes(ID, PackageNode, node(X, A1)).
% Special cases % For node flag sources we need to look at the condition_set of the source, since it is the dependent
special_case(ID, Name, A1, A2, A3) :- imposed_constraint(ID, Name, A1, A2, A3), Name == "node_flag_source". % of the package on which I want to impose the constraint
special_case(ID, Name, A1, A2, A3) :- imposed_constraint(ID, Name, A1, A2, A3), Name == "depends_on". attr("node_flag_source", node(X, A1), A2, node(Y, A3))
attr("node_flag_source", node(NodeID, Package), A2, node(0, A3)) :- impose(ID, node(NodeID, Package)), imposed_constraint(ID, "node_flag_source", Package, A2, A3). :- impose(ID, node(X, A1)),
attr("depends_on", node(NodeID, Package), node(0, A2), A3) :- impose(ID, node(NodeID, Package)), imposed_constraint(ID, "depends_on", Package, A2, A3). imposed_constraint(ID, "node_flag_source", A1, A2, A3),
condition_set(node(Y, A3), node(X, A1)).
% Here we can't use the condition set because it's a recursive definition, that doesn't define the
% node index, and leads to unsatisfiability. Hence we say that one and only one node index must
% satisfy the dependency.
1 { attr("depends_on", node(X, A1), node(0..Y-1, A2), A3) : max_nodes(A2, Y) } 1
:- impose(ID, node(X, A1)),
imposed_constraint(ID, "depends_on", A1, A2, A3).
% we cannot have additional variant values when we are working with concrete specs % we cannot have additional variant values when we are working with concrete specs
:- attr("node", node(ID, Package)), :- attr("node", node(ID, Package)),
@ -336,26 +440,22 @@ error(1, Msg)
% provider for that virtual then it depends on the provider % provider for that virtual then it depends on the provider
attr("depends_on", PackageNode, ProviderNode, Type) attr("depends_on", PackageNode, ProviderNode, Type)
:- dependency_holds(PackageNode, Virtual, Type), :- dependency_holds(PackageNode, Virtual, Type),
provider(ProviderNode, node(0, Virtual)), provider(ProviderNode, node(VirtualID, Virtual)),
not external(PackageNode). not external(PackageNode).
attr("virtual_on_edge", PackageNode, ProviderNode, Virtual) attr("virtual_on_edge", PackageNode, ProviderNode, Virtual)
:- dependency_holds(PackageNode, Virtual, Type), :- dependency_holds(PackageNode, Virtual, Type),
provider(ProviderNode, node(0, Virtual)), provider(ProviderNode, node(VirtualID, Virtual)),
not external(PackageNode). not external(PackageNode).
% dependencies on virtuals also imply that the virtual is a virtual node % dependencies on virtuals also imply that the virtual is a virtual node
attr("virtual_node", node(0, Virtual)) 1 { attr("virtual_node", node(0..X-1, Virtual)) : max_nodes(Virtual, X) }
:- dependency_holds(PackageNode, Virtual, Type), :- dependency_holds(PackageNode, Virtual, Type),
virtual(Virtual), not external(PackageNode). virtual(Virtual), not external(PackageNode).
% If there's a virtual node, we must select one and only one provider. % If there's a virtual node, we must select one and only one provider.
% The provider must be selected among the possible providers. % The provider must be selected among the possible providers.
{ provider(node(0..X-1, Package), node(VirtualID, Virtual))
: facts(Package, possible_provider(Virtual)), max_nodes(Package, X) }
:- attr("virtual_node", node(VirtualID, Virtual)).
error(100, "Cannot find valid provider for virtual {0}", VirtualNode) error(100, "Cannot find valid provider for virtual {0}", VirtualNode)
:- attr("virtual_node", VirtualNode), :- attr("virtual_node", VirtualNode),
not provider(_, VirtualNode). not provider(_, VirtualNode).
@ -376,10 +476,11 @@ attr("root", PackageNode) :- attr("virtual_root", VirtualNode), provider(Package
% for environments that are concretized together (e.g. where we % for environments that are concretized together (e.g. where we
% asks to install "mpich" and "hdf5+mpi" and we want "mpich" to % asks to install "mpich" and "hdf5+mpi" and we want "mpich" to
% be the mpi provider) % be the mpi provider)
provider(PackageNode, node(0, Virtual)) :- attr("node", PackageNode), virtual_condition_holds(PackageNode, Virtual). 1 { provider(PackageNode, node(0..X-1, Virtual)) : max_nodes(Virtual, X) } 1 :- attr("node", PackageNode), virtual_condition_holds(PackageNode, Virtual).
:- 2 { provider(PackageNode, VirtualNode) }, attr("virtual_node", VirtualNode).
% The provider provides the virtual if some provider condition holds. % The provider provides the virtual if some provider condition holds.
virtual_condition_holds(node(ProviderID, Provider), Virtual) :-virtual_condition_holds(ID, node(ProviderID, Provider), Virtual). virtual_condition_holds(node(ProviderID, Provider), Virtual) :- virtual_condition_holds(ID, node(ProviderID, Provider), Virtual).
virtual_condition_holds(ID, node(ProviderID, Provider), Virtual) :- virtual_condition_holds(ID, node(ProviderID, Provider), Virtual) :-
facts(Provider, provider_condition(ID, Virtual)), facts(Provider, provider_condition(ID, Virtual)),
condition_holds(ID, node(ProviderID, Provider)), condition_holds(ID, node(ProviderID, Provider)),
@ -387,7 +488,7 @@ virtual_condition_holds(ID, node(ProviderID, Provider), Virtual) :-
% A package cannot be the actual provider for a virtual if it does not % A package cannot be the actual provider for a virtual if it does not
% fulfill the conditions to provide that virtual % fulfill the conditions to provide that virtual
:- provider(PackageNode, node(0, Virtual)), :- provider(PackageNode, node(VirtualID, Virtual)),
not virtual_condition_holds(PackageNode, Virtual), not virtual_condition_holds(PackageNode, Virtual),
internal_error("Virtual when provides not respected"). internal_error("Virtual when provides not respected").
@ -398,13 +499,10 @@ virtual_condition_holds(ID, node(ProviderID, Provider), Virtual) :-
% A provider may have different possible weights depending on whether it's an external % A provider may have different possible weights depending on whether it's an external
% or not, or on preferences expressed in packages.yaml etc. This rule ensures that % or not, or on preferences expressed in packages.yaml etc. This rule ensures that
% we select the weight, among the possible ones, that minimizes the overall objective function. % we select the weight, among the possible ones, that minimizes the overall objective function.
1 { provider_weight(DependencyNode, VirtualNode, Weight, Reason) : 1 { provider_weight(DependencyNode, VirtualNode, Weight) :
possible_provider_weight(DependencyNode, VirtualNode, Weight, Reason) } 1 possible_provider_weight(DependencyNode, VirtualNode, Weight, _) } 1
:- provider(DependencyNode, VirtualNode), internal_error("Package provider weights must be unique"). :- provider(DependencyNode, VirtualNode), internal_error("Package provider weights must be unique").
% Get rid or the reason for enabling the possible weight (useful for debugging)
provider_weight(DependencyNode, VirtualNode, Weight) :- provider_weight(DependencyNode, VirtualNode, Weight, _).
% A provider that is an external can use a weight of 0 % A provider that is an external can use a weight of 0
possible_provider_weight(DependencyNode, VirtualNode, 0, "external") possible_provider_weight(DependencyNode, VirtualNode, 0, "external")
:- provider(DependencyNode, VirtualNode), :- provider(DependencyNode, VirtualNode),
@ -412,15 +510,15 @@ possible_provider_weight(DependencyNode, VirtualNode, 0, "external")
% A provider mentioned in packages.yaml can use a weight % A provider mentioned in packages.yaml can use a weight
% according to its priority in the list of providers % according to its priority in the list of providers
possible_provider_weight(node(DependencyID, Dependency), node(0, Virtual), Weight, "packages_yaml") possible_provider_weight(node(DependencyID, Dependency), node(VirtualID, Virtual), Weight, "packages_yaml")
:- provider(node(DependencyID, Dependency), node(0, Virtual)), :- provider(node(DependencyID, Dependency), node(VirtualID, Virtual)),
depends_on(node(ID, Package), node(DependencyID, Dependency)), depends_on(node(ID, Package), node(DependencyID, Dependency)),
facts(Package, provider_preference(Virtual, Dependency, Weight)). facts(Package, provider_preference(Virtual, Dependency, Weight)).
% A provider mentioned in the default configuration can use a weight % A provider mentioned in the default configuration can use a weight
% according to its priority in the list of providers % according to its priority in the list of providers
possible_provider_weight(node(DependencyID, Dependency), node(0, Virtual), Weight, "default") possible_provider_weight(node(DependencyID, Dependency), node(VirtualID, Virtual), Weight, "default")
:- provider(node(DependencyID, Dependency), node(0, Virtual)), :- provider(node(DependencyID, Dependency), node(VirtualID, Virtual)),
default_provider_preference(Virtual, Dependency, Weight). default_provider_preference(Virtual, Dependency, Weight).
% Any provider can use 100 as a weight, which is very high and discourage its use % Any provider can use 100 as a weight, which is very high and discourage its use
@ -718,14 +816,14 @@ external_with_variant_set(node(NodeID, Package), Variant, Value)
variant_default_value(Package, Variant, Value) variant_default_value(Package, Variant, Value)
:- facts(Package, variant_default_value_from_package_py(Variant, Value)), :- facts(Package, variant_default_value_from_package_py(Variant, Value)),
not variant_default_value_from_packages_yaml(Package, Variant, _), not variant_default_value_from_packages_yaml(Package, Variant, _),
not attr("variant_default_value_from_cli", node(0, Package), Variant, _). not attr("variant_default_value_from_cli", node(root_node_id, Package), Variant, _).
variant_default_value(Package, Variant, Value) variant_default_value(Package, Variant, Value)
:- variant_default_value_from_packages_yaml(Package, Variant, Value), :- variant_default_value_from_packages_yaml(Package, Variant, Value),
not attr("variant_default_value_from_cli", node(0, Package), Variant, _). not attr("variant_default_value_from_cli", node(root_node_id, Package), Variant, _).
variant_default_value(Package, Variant, Value) :- variant_default_value(Package, Variant, Value) :-
attr("variant_default_value_from_cli", node(0, Package), Variant, Value). attr("variant_default_value_from_cli", node(root_node_id, Package), Variant, Value).
% Treat 'none' in a special way - it cannot be combined with other % Treat 'none' in a special way - it cannot be combined with other
% values even if the variant is multi-valued % values even if the variant is multi-valued
@ -883,12 +981,12 @@ node_target_weight(node(ID, Package), Weight)
% compatibility rules for targets among nodes % compatibility rules for targets among nodes
node_target_match(ParentNode, DependencyNode) node_target_match(ParentNode, DependencyNode)
:- depends_on(ParentNode, DependencyNode), :- attr("depends_on", ParentNode, DependencyNode, Type), Type != "build",
attr("node_target", ParentNode, Target), attr("node_target", ParentNode, Target),
attr("node_target", DependencyNode, Target). attr("node_target", DependencyNode, Target).
node_target_mismatch(ParentNode, DependencyNode) node_target_mismatch(ParentNode, DependencyNode)
:- depends_on(ParentNode, DependencyNode), :- attr("depends_on", ParentNode, DependencyNode, Type), Type != "build",
not node_target_match(ParentNode, DependencyNode). not node_target_match(ParentNode, DependencyNode).
% disallow reusing concrete specs that don't have a compatible target % disallow reusing concrete specs that don't have a compatible target
@ -1186,9 +1284,14 @@ build_priority(PackageNode, 0) :- attr("node", PackageNode), not optimize_for_
% is displayed (clingo doesn't display sums over empty sets by default) % is displayed (clingo doesn't display sums over empty sets by default)
% Try hard to reuse installed packages (i.e., minimize the number built) % Try hard to reuse installed packages (i.e., minimize the number built)
opt_criterion(100, "number of packages to build (vs. reuse)"). opt_criterion(110, "number of packages to build (vs. reuse)").
#minimize { 0@110: #true }.
#minimize { 1@110,PackageNode : build(PackageNode), optimize_for_reuse() }.
opt_criterion(100, "number of nodes from the same package").
#minimize { 0@100: #true }. #minimize { 0@100: #true }.
#minimize { 1@100,PackageNode : build(PackageNode), optimize_for_reuse() }. #minimize { ID@100,Package : attr("node", node(ID, Package)) }.
#minimize { ID@100,Package : attr("virtual_node", node(ID, Package)) }.
#defined optimize_for_reuse/0. #defined optimize_for_reuse/0.
% A condition group specifies one or more specs that must be satisfied. % A condition group specifies one or more specs that must be satisfied.
@ -1369,34 +1472,6 @@ opt_criterion(5, "non-preferred targets").
build_priority(PackageNode, Priority) build_priority(PackageNode, Priority)
}. }.
%-----------------
% Domain heuristic
%-----------------
#heuristic literal_solved(ID) : literal(ID). [1, sign]
#heuristic literal_solved(ID) : literal(ID). [50, init]
#heuristic attr("hash", PackageNode, Hash) : attr("root", PackageNode). [45, init]
#heuristic attr("version", node(0, Package), Version) : facts(Package, version_declared(Version, 0)), attr("root", node(0, Package)). [40, true]
#heuristic version_weight(node(0, Package), 0) : facts(Package, version_declared(Version, 0)), attr("root", node(0, Package)). [40, true]
#heuristic attr("variant_value", node(0, Package), Variant, Value) : variant_default_value(Package, Variant, Value), attr("root", node(0, Package)). [40, true]
#heuristic attr("node_target", node(0, Package), Target) : facts(Package, target_weight(Target, 0)), attr("root", node(0, Package)). [40, true]
#heuristic node_target_weight(node(0, Package), 0) : attr("root", node(0, Package)). [40, true]
#heuristic node_compiler(node(0, Package), CompilerID) : default_compiler_preference(ID, 0), compiler_id(ID), attr("root", node(0, Package)). [40, true]
#heuristic provider(PackageNode, VirtualNode) : possible_provider_weight(PackageNode, VirtualNode, 0, _), attr("virtual_node", VirtualNode). [30, true]
#heuristic provider_weight(Package, Virtual, 0, R) : possible_provider_weight(Package, Virtual, 0, R), attr("virtual_node", Virtual). [30, true]
#heuristic attr("node", Package) : possible_provider_weight(Package, Virtual, 0, _), attr("virtual_node", Virtual). [30, true]
#heuristic attr("version", node(ID, Package), Version) : facts(Package, version_declared(Version, 0)), attr("node", node(ID, Package)). [20, true]
#heuristic version_weight(node(ID, Package), 0) : facts(Package, version_declared(Version, 0)), attr("node", node(ID, Package)). [20, true]
#heuristic attr("node_target", node(ID, Package), Target) : facts(Package, target_weight(Target, 0)), attr("node", node(ID, Package)). [20, true]
#heuristic node_target_weight(Package, 0) : attr("node", Package). [20, true]
#heuristic node_compiler(node(ID, Package), ID) : default_compiler_preference(ID, 0), compiler_id(ID), attr("node", node(NodeID, Package)). [15, true]
#heuristic attr("variant_value", node(ID, Package), Variant, Value) : variant_default_value(Package, Variant, Value), attr("node", node(ID, Package)). [10, true]
#heuristic attr("node_os", PackageNode, OS) : buildable_os(OS). [10, true]
%----------- %-----------
% Notes % Notes
%----------- %-----------

View file

@ -0,0 +1,28 @@
% 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)
%-----------------
% Domain heuristic
%-----------------
#heuristic literal_solved(ID) : literal(ID). [1, sign]
#heuristic literal_solved(ID) : literal(ID). [50, init]
#heuristic attr("hash", node(0, Package), Hash) : literal(_, "root", Package). [45, init]
#heuristic attr("root", node(0, Package)) : literal(_, "root", Package). [45, true]
#heuristic attr("node", node(0, Package)) : literal(_, "root", Package). [45, true]
#heuristic attr("node", node(0, Package)) : literal(_, "node", Package). [45, true]
#heuristic attr("version", node(0, Package), Version) : facts(Package, version_declared(Version, 0)), attr("root", node(0, Package)). [40, true]
#heuristic version_weight(node(0, Package), 0) : facts(Package, version_declared(Version, 0)), attr("root", node(0, Package)). [40, true]
#heuristic attr("variant_value", node(0, Package), Variant, Value) : variant_default_value(Package, Variant, Value), attr("root", node(0, Package)). [40, true]
#heuristic attr("node_target", node(0, Package), Target) : facts(Package, target_weight(Target, 0)), attr("root", node(0, Package)). [40, true]
#heuristic node_target_weight(node(0, Package), 0) : attr("root", node(0, Package)). [40, true]
#heuristic node_compiler(node(0, Package), CompilerID) : default_compiler_preference(ID, 0), compiler_id(ID), attr("root", node(0, Package)). [40, true]
#heuristic version_weight(node(0, Package), 0) : attr("node", node(0, Package)). [20, true]
#heuristic attr("node_target", node(0, Package), Target) : facts(Package, target_weight(Target, 0)), attr("node", node(0, Package)). [20, true]
#heuristic node_target_weight(PackageNode, 0) : attr("node", PackageNode). [20, true]
#heuristic node_compiler(node(0, Package), ID) : default_compiler_preference(ID, 0), compiler_id(ID), attr("node", node(0, Package)). [15, true]