• No results found

4.4 Optimizing the Relevant Grounding for Constraints

4.4.1 Implementation

We implemented our optimization method that detects and removes independent atoms from conjunctive constraints as a processing step that takes place after grounding is completed. It compacts the AND-OR graph associated to the relevant ground LP and the ground instances of the constraints. Our approach is similar to the Pattern-based compaction presented in Chapter 3.

In order to implement our method, first we modified the AND-OR graph generation method. Namely, we added an additional marker for every node in the AND-OR graph to keep track of the origin of that node. If a node is constructed from the ground program, we mark it with “grounding”. If the node is build from the constraints then we mark it with “constraint”. If an OR or a

terminal node7 needs to be added that already exists and is marked differently

than the current origin, then its marker is substituted with “disjunction”; if the node is an AND node, then its marker is substituted with “conjunction”. This extension of the AND-OR graph doesn’t increase the memory complexity as it is just a different labeling of the nodes.

The algorithm we implement applies two steps: (i) detecting if the constraints and the queries are independent (Theorem 4.1), and if yes removal of the nodes and edges from the AND-OR graph originating from the constraints presented in Algorithm 3; and (ii) detection of conjuncts irrelevant from the query (Theorem 4.2) that are then removed form the AND-OR graph given in Algorithm 4. We implement an AND-OR graph as a collection of edges between nodes. We call an edge that connects a parent OR node with a child node an

OR edge and an edge that connects a parent AND node with a child node an AND edge. Each edge is stored as a Prolog fact. Also terminal nodes are stored

as facts.

Algorithm 3: Algorithm for detecting and removing independent constraints.

Data: An AND-OR graph

Result: An AND-OR graph

detect_independent_constraint ← \+ terminal_node(Terminal, _)/disjunction, \+ or_edge(OR, _)/disjunction, \+ and_node(AND, _)/conjunction. simplify_graph_independent_constraint ← detect_independent_constraint, each(terminal_node(Terminal, _)/constraint, retract(terminal_node(Terminal, _)/constraint)),

each(or_edge(OR, _)/constraint, retract(or_edge(OR, _)/constraint)), each(and_node(AND, _)/constraint, retract(and_edge(AND, _)/constraint)).

Algorithm 4:Algorithms for detecting and removing of independent atoms from conjunctive constraints.

Data: An AND-OR graph

Result: An AND-OR graph

detect_independent_conjunction(NodeA, RefinedChildren) ←

and_edge(NodeA, _)/constraint, all(Terminal, ( and_edge(NodeA, Terminal)/constraint, terminal_node(Terminal, _)/constraint, 6 ∃or_edge(_, Terminal)/_ ), Children), Children 6= ∅, 6 ∃disjunctive_predecessors(NodeA), get_all_and_edge_sets(ChildSets),

refine_cluster(ChildSets, Children, RefinedChildren).

disjunctive_predecessors(NodeB) ←

or_edge(NodeA, NodeB).

disjunctive_predecessors(NodeB) ←

and_edge(NodeA, NodeB), disjunctive_predecessors(NodeA).

update_graph(NodeA, Children) ←

each(Child ∈ Children, (retract(terminal(Child, _)/constraint), retract(and_edge(NodeA, Child)/constraint)) ), update_graph_empty_conjunction(NodeA). update_graph_empty_conjunction(NodeA) ←

all(Child, and_edge(NodeA, Child)/_, []), retractall(or_edge(ParentNode, NodeA)/_)).

Because we implement the AND-OR graph as a collection of edges our analysis of the complexity determines a bound with respect to the number of edges. We performed similar analysis for the detection and compaction

algorithm in Chapter 3. For an arbitrary AND-OR graph G let us

denote with Nor,constraint, Nor,grounding, Nor,disjunctionthe number of OR edges

that originate from the constraints, the ground LP or from both; with

Nand,constraint, Nand,grounding, Nand,conjunctionthe number of AND edges that

originate from the constraints, the ground LP or from both; and with

Nterm,constraint, Nterm,grounding, Nterm,disjunctionthe number of terminal nodes

that originate from the constraints, the ground LP or from both, respectively.

With Nor, Nand and Nterm we denote all OR edges, AND edges and terminal

nodes regardless the origin.

To detect whether a constraint8 is independent from the query we first need

to check all terminal nodes that originate from both the constraints and the ground LP; if there are no such nodes we need to look for OR edges that originate from both constraints and ground LP; and if there are no such edges 8Although we speak of one constraint, given the semantics of cProbLog a set of constraints

forms a conjunction of all that constraints. That is why we can assume that there is only one constraint, without loss of generality.

we need to test the AND edges. That is done in a constant time: O(1). To remove the subgraph associated with the constraint the complexity is:

O(Nor,constraint+ Nand,constraint+ Nterm,constraint).

In the detection step of Algorithm 4 we want to find out that we have a conjunction of literals that are terminal nodes and are not children of OR nodes. To do so we need to first collect all AND edges between an AND node that originates from the constraint and a terminal node, such that the terminal node does not participate in OR edges. Then we need to traverse the graph and check if any of the predecessors is an OR node. The total

complexity to detect is O(Nand,constraint· Nterminal,constraint· Nor). To update

the graph first we remove the edges from the detected node and its children. Then we also need to update the graph by removing all AND edges that point to non existing terminal nodes. We also need to remove any other edges that point to nodes that have already been removed. The complexity is:

O(Nor,constraints+ Nand,constraints+ Nterm,constraints).