=Paper=
{{Paper
|id=Vol-3428/paper11
|storemode=property
|title=Compilation-based Techniques for Evaluating Normal Logic Programs Under the Well-founded Semantics
|pdfUrl=https://ceur-ws.org/Vol-3428/paper11.pdf
|volume=Vol-3428
|authors=Andrea Cuteri,Giuseppe Mazzotta,Francesco Ricca
|dblpUrl=https://dblp.org/rec/conf/cilc/CuteriMR23
}}
==Compilation-based Techniques for Evaluating Normal Logic Programs Under the Well-founded Semantics==
Compilation-based Techniques for Evaluating Normal
Logic Programs Under the Well-founded Semantics
Andrea Cuteri, Giuseppe Mazzotta and Francesco Ricca
University of Calabria, Rende 87036, Italy
Abstract
Recent studies have demonstrated that compilation-based techniques can be beneficial for evaluating
Datalog and ASP programs. In this paper, we develop a compiler that is able to generate solvers for
normal logic programs under the well-founded semantics. The proposed system has been evaluated
on different settings and preliminary results highlight significant improvements in the evaluation of
non-stratified programs.
Keywords
Logic Programming, Well-founded semantics, Compilation
1. Introduction
Logic programming is a declarative programming paradigm that can be used to model complex
problems in terms of logical implications [1]. Logic programs are often evaluated by means of
general-purpose systems that implement a given semantics [2, 3]. The need for handling with
the same algorithm any possible input, in some cases, makes it impossible to apply specific
optimizations that would work only for a subclass of programs. Thus, considerable speedups
can be obtained by using ad-hoc evaluation procedures for the program in input. Following
this consideration, compilation-based techniques have been recently proposed to speed up the
evaluation of Datalog [4] and Answer Set Programming (ASP) [5, 6]. In particular, the idea
behind this approach is to compile the input program into a custom system that is optimized
by exploiting the syntactic properties of the modeled program and can be used for evaluating
different instances of the compiled program. Concerning Datalog, the system soufflΓ© [4] was
demonstrated to be very effective, especially for solving tasks connected with software develop-
ment, but also other prototypical systems were revealed to be very promising [7]. Concerning
ASP, compilation techniques have been successfully employed for the evaluation of grounding-
intensive ASP programs outperforming state-of-the-art ASP solvers [6, 8] and reducing both
time and memory consumption. However, none of the aforementioned systems support the
well-known well-founded semantics [9] when no restriction is posed on the usage of negation.
In this paper, we propose a compilation-based technique for evaluating normal logic programs
under the well-founded semantics and present a system that is able to generate ad-hoc solvers
for programs.
CILCβ23: 38th Italian Conference on Computational Logic, June 21β23, 2023, Udine, Italy
$ cuteri.andrea@gmail.com (A. Cuteri); giuseppe.mazzotta@unical.it (G. Mazzotta); ricca@mat.unical.it (F. Ricca)
Β© 2023 Copyright for this paper by its authors. Use permitted under Creative Commons License Attribution 4.0 International (CC BY 4.0).
CEUR
Workshop
Proceedings
http://ceur-ws.org
ISSN 1613-0073
CEUR Workshop Proceedings (CEUR-WS.org)
An empirical evaluation of the proposed approach has been conducted on hard bench-
marks [10, 11]. Obtained results demonstrate that the proposed approach is competitive with
existing implementations capable of evaluating both Datalog programs (i.e. positive programs)
and program with negation. Notably, our approach outperforms dlv2 [12] on normal logic
programs with not-stratified negation.
2. Logic Programs Under the Well-founded Semantics
In this section, some preliminaries are provided on normal logic programs under the well-
founded semantics [9].
2.1. Syntax
A term is a constant or a variable. Constants are strings starting with lowercase letter or integers
instead variables are terms starting with uppercase letter. An atom π is an expression of the form
π(π‘1 , ..., π‘π ) where π is a predicate of arity π and π‘1 , ..., π‘π are terms. If all the terms are constants
then π is a ground atom. A literal is an atom π, or its negation πππ‘ π where πππ‘ represents default
negation. A literal is positive if it is of the form π, negative otherwise. Given a literal π, the com-
plement of π, denoted by π, is π if π = πππ‘ π, πππ‘ π otherwise. A rule π is an expression of the form:
β β π1 , ..., ππ , πππ‘ ππ+1 , ..., πππ‘ ππ .
where β is an atom referred to as head, π»π , π1 , ..., πππ‘ ππ is a conjunction of literals referred
to as body of the rule, π΅π , and π > 0. If π = 0 then π is a fact. A program Ξ is a set of rules.
Given a set of literals π΅, π΅ + (resp. π΅ β ) denotes the set of positive (resp. negative) literals in
π΅. The dependency graph of a program Ξ , πΊΞ , is a directed labeled graph where the nodes
are predicates appearing in Ξ and the set of the edges contains a positive (resp. negative) edge
(π’, π£) if exists a rule π£ β π΅ β Ξ where π’ appears in π΅ + (resp. π’ appears in π΅ β ). Ξ is said to
be Datalog if it does not contain any negative literals; Datalog with stratified negation if πΊΞ
does not contain cycles with negative edges.
2.2. Well-founded Semantics
Given a program Ξ , the Herbrand Universe is the set of constants appearing in Ξ ; the Herbrand
Base, π΅Ξ , is the set of possible ground atoms that can be built using predicate in Ξ and
constants in the Herbrand Universe. Given a rule π β Ξ , ππππ’ππ(π) represents all possible
instantiations of π βοΈ
replacing variables with constants in the Herbrand Universe. Given a program
Ξ , ππππ’ππ(Ξ ) = πβΞ ππππ’ππ(π). An interpretation πΌ is a set of literals whose atoms belong
to π΅Ξ . πΌ is consistent if for each literal π β πΌ, πππ‘ π β / πΌ. πΌ is total if for each atom π β π΅Ξ
either π or πππ‘ π belongs to πΌ. Given a consistent interpretation πΌ, a literal π is true (resp. false)
w.r.t. πΌ if π β πΌ (resp. πππ‘ π β πΌ). A literal is undefined w.r.t. πΌ if it is neither true nor false.
A conjunction of literals is true w.r.t. πΌ if all the literals are true w.r.t. πΌ; it is false if at least
one literal is false w.r.t. πΌ; it is undefined otherwise. A set of atoms, πΞ , is an unfounded set
w.r.t. πΌ if for every atom π β πΞ and for every rule π β ππππ’ππ(Ξ ) such that π»π = π, π΅π is
false w.r.t. πΌ or π΅π contains some positive literals whose atoms belong to πΞ . Intuitively, all
rules defining atoms in πΞ have a false body or depend on atoms in πΞ and so they cannot be
inferred as true. The greatest unfounded set, πΞ (πΌ), is the union of possible unfounded sets
πΞ . Let πΞ (πΌ) be a transformation defined as the set of atoms π such that there exists a rule
π β ππππ’ππ(Ξ ) where π»π = π and π΅π is true w.r.t. πΌ, we define πΞ (πΌ) = πΞ (πΌ) βͺ Β¬πΞ (πΌ)
where Β¬πΞ (πΌ) = {πππ‘ π | π β πΞ (πΌ)}. Let πΌ0 = β
, πΌπΌ+1 = π²π (πΌπΌ ), with πΌ β₯ 0, the (partial)
well-founded model is defined as π²π = πΌπ½ where π½ β₯ 0 is the smallest ordinal such that
πΌπ½ = πΌπ½+1 . Basically, π²π is the least fixed point π²π [9]
Algorithm 1 compileProgram
Input : A normal program π
Output : Prints evaluation procedure for π
1 begin
2 βͺdef πππππ’π‘ππ ππππΉ ππ’ππππ(π πππ‘π )β«
3 βͺ πΌ = π πππ‘π β«
4 βͺ β¬ = β
β«
5 π·πΊ = πππππ’π‘ππ·πΊ(π )
6 ππΆπΆ = πππππ’π‘πππΆπΆ(π·πΊ)
7 for all πΆ β ππΆπΆ do
8 βͺπ_π π‘πππ = πΉ = β
β«
9 for all π β πππππ’π‘ππ
π’πππ πΉ πππΆππππππππ‘(π, πΆ) do
10 ππππππππ
π’πππΉ πππ»ππππ·ππππ£ππ‘πππ(π΅π , π»π , πΆ)
11 βͺ while π_π π‘πππ ΜΈ= β
doβ«
12 βͺ π π‘πππ‘ππ = π_π π‘πππ.πππ()β«
13 βͺ switch ππππ(π π‘πππ‘ππ)β«
14 for all π β πππππ’π‘ππ
πππ’ππ ππ£ππ
π’πππ (π, πΆ) do
15 for all π β [1, . . . , |π΅π |] do
16 if ππππ(π΅π [π]) β πΆ β§ π΅π [π] is positive literal then
17 βͺcase Jππππ(π΅π [π])K β«
18 βͺ π = πβ«
19 for all π β 1, ..., |π‘ππ(π΅π [π])| do
20 if π‘ππ(π΅π [π])[π] is variable then
21 βͺ π = π βͺ { Jπ‘ππ(π΅π [π])[π]K β¦β π‘ππ(π π‘πππ‘ππ)[ JπK ]}β«
22 ππππππππ
π’πππΉ πππ»ππππ·ππππ£ππ‘πππ(π΅π β {π΅π [π]}, π»π , πΆ)
23 βͺdoneβ«
24 βͺdoβ«
25 βͺ ππππ = β₯β«
26 βͺ for π π‘πππ‘ππ β β¬ doβ«
27 βͺ π‘ππ’π = π’ππππ = β₯β«
28 βͺ π π‘πππ‘ππ = π _π π‘πππ.πππ()β«
29 βͺ switch ππππ(π π‘πππ‘ππ)β«
30 ππππππππ
π’πππ πΉ ππππ’πππππ‘π·ππππ£ππ‘πππ(πππππ’π‘ππ
πππ’ππ ππ£ππ
π’πππ (π, πΆ), πΆ)
31 βͺ doneβ«
32 βͺwhile ππππ == β€ β«
33 βͺend def β«
Algorithm 2 compileRuleForHeadDerivation
Input : A list of literals π΅, an atom π, a set of predicates πΆ
Output : Prints a procedure that instantiates π΅ and derives new atoms matching π
1 begin
2 ππππππππ
π’πππ΅πππ¦(π΅, πΆ)
3 πππππ‘π»ππππ·ππππ£ππ‘πππ(π΅, π)
4 for all π β [|π΅|, . . . , 1] do
5 if π΅[π] is positive literal then
6 βͺ π = π JπK β«
7 βͺdoneβ«
8 else
9 βͺfiβ«
3. Compilation of Well-founded Semantics
In this section, we describe the compilation procedure for generating an ad-hoc solver for
an input program Ξ . In particular, proposed algorithms (see Algorithms 1-5) describe the
compilation of a normal program with not-stratified negation. However, our approach is also
able to generate simplified code for the case of Datalog programs with stratified negation that
is indeed a particular case. Reported algorithms follow the syntactic convention used in [8].
To recall, the code enclosed between βͺβ« is printed by the compiler as it is. Instead, the code
enclosed in JK , is first substituted with its run-time value and then is printed. For example, let
π΅π [π] = π(π), Algorithm 1 at line 17 prints βcase βπβ :β.
Description of the Compilation Algorithms. As the first step, the compiler builds the
dependency graph of Ξ (Alg. 1 lines 5-6) and computes its strongly connected components
SCCs, πΆ1 , ..., πΆπ , that give us a topological order of πΊΞ such that no paths exist from πΆπ to πΆπ
if π < π. By following that order, the compiler produces for each component πΆ the code that
evaluates the rules defining atoms in πΆ (i.e., whose predicate belongs to πΆ), referred to as ππΆ .
Then, each rule π is compiled into a sub-procedure that iterates over possible instantiations of
π΅π that are either true or undefined w.r.t. πΌ βͺ β¬ and, successively, derives π»π (Alg. 1 lines 9-10).
Such sub-procedures are generated by means of Algorithm 2. Algorithm 2 starts by calling
Algorithm 3 that prints different nested join loops or if statements for each literal in π΅π . These
nested blocks implement iterations over possible rule instantiations (Alg. 3 lines 3-13). Inside
the deepest block, the code that collects negative dependencies within the component πΆ into
the set ππ is printed (Alg. 3 lines 15-19). Then Algorithm 2 prints the code that derives new
atoms matching π»π by calling Algorithm 4. In particular, the code generated by Algorithm 4
checks if the current body (i.e. π‘1 , Β· Β· Β· , π‘π ) is true w.r.t. πΌ βͺ β¬. In particular, if all positive literals
belong to πΌ, no negative literals are undefined (i.e. Β¬πβ β© β¬), and no negative literals in the
same component occur in the current body (i.e. ππ = β
), then all literals π‘1 , Β· Β· Β· , π‘π are true
w.r.t. πΌ. Thus, if it is the case then the head of the rule is derived as true, otherwise it is derived
as undefined (code generated by lines 5 and 10 of Alg. 4). In both cases, derived atoms are
collected into the derivation stack in order to be used in the second derivation phase. As the last
step, for each literal π β π΅π , if π is a positive literal then a nested for-loop is closed by restoring
the variable substitution π (Alg. 2 lines 5-7). Otherwise, an if-statement scope is closed (Alg. 2
lines 8-9). The second derivation scenario is generated by looking at recursive rules defining
atoms whose predicate belongs to πΆ. In this case, the generated procedure will consume literals
collected into π_π π‘πππ and, for each of them, different sub-procedures are executed according
to the predicate name of the consumed literal (Alg. 1 lines 11-13). In particular, the compiler, for
each rule π, generates different switch-cases for each literal π β π΅π+ whose predicate belongs
to πΆ (Alg. 1 lines 15-22). Inside each case, a sub-procedure that evaluates π starting from a
literal, π π‘πππ‘ππ, that matches π is generated. Each sub-procedure starts with the initialization of
a variable substitution π from variables in π to constant in π π‘πππ‘ππ (Alg. 1 lines 18-21) and then
contains the code that evaluates the remaining part of π΅π , generated by Algorithm 2. In this
way, the generated procedure is able to simulate a semi-naive evaluation of recursive rules. Out
of the while-loop scope, the compiler generates the code that derives, if it is possible, undefined
atoms either as true or false (Alg. 1 lines 24-32). At this point, atoms in π΅Ξ with predicates
in πΆ that do not belong to πΌ βͺ π΅ are considered false since no rule instantiations that can
derive them exists. Thus, the generated procedure, in this case, will iterate until some undefined
atoms (i.e. atoms belonging to β¬) are derived either as true or false. For each iteration, the
generated procedure evaluates possible rules defining remaining atoms in β¬ and so different
switch-cases, one for each recursive rule π, are generated. Each case contains the procedure
that evaluates a rule π starting from an undefined atom π π‘πππ‘ππ that can be substituted to π»π .
These sub-procedures are generated by Algorithm 5 that, for each switch-case, prints the code
that initializes a variable substitution π from variables in π»π to constants in π π‘πππ‘ππ (Alg. 5
Algorithm 3 compileRuleBody
Input : A list of literals π΅, a set of predicate πΆ
Output : Prints the instantiation procedure for the rule body π΅
1 begin
2 βͺπ = π β«
3 for all π β 1, ..., |π΅| do
4 βͺπ JπK = π β«
5 if π΅[π] is a positive literal then
6 βͺπ JπK = {π β (πΌ βͺ β¬) | πππ‘πβ(π( Jπ΅[π]K ), π)}β«
7 βͺfor all π‘ JπK β π JπK doβ«
8 for all π β 1, ..., |π‘ππ(π΅[π])| do
9 if π‘ππ(π΅[π])[π] is variable then
10 βͺπ = π βͺ { Jπ‘ππ(π΅[π])[π]K β¦β π‘ππ(π‘ JπK )[ JπK ]}β«
11 else
12 βͺπ‘ JπK = π( Jπ΅[π]K )β«
13 βͺif π‘ JπK β
/ πΌ thenβ«
14 βͺπ = β
β«
15 βͺππ = β
β«
16 for all π β 1, ..., |π΅| do
17 βͺπ = π βͺ {π‘ JπK }β«
18 if π΅[π] is a negative literal β§ ππππ(π΅[π]) β πΆ then
19 βͺππ = ππ βͺ {π‘ JπK }β«
Algorithm 4 printHeadDerivation
Input : An atom π
Output : Prints the derivation procedure for new atoms matching π
1 begin
2 βͺβ = π( JπK )β«
+ β
3 βͺif ππ == β
β§ π β πΌ β§ (Β¬π β© β¬) == β
thenβ«
4 βͺ π_π π‘πππ = π_π π‘πππ βͺ {β}β«
5 βͺ πΌ = πΌ βͺ {β}β«
6 βͺ β¬ = β¬ β {β}β«
7 βͺelseβ«
8 βͺ if β β
/ (πΌ βͺ β¬)β«
9 βͺ π_π π‘πππ = π_π π‘πππ βͺ {β}β«
10 βͺ β¬ = β¬ βͺ {β}β«
11 βͺ fiβ«
12 βͺfiβ«
lines 4-7) and then prints the nested block needed for evaluating π΅π by means of Algorithm 3.
Then, inside the last nested level, the code for head derivation is generated (Alg. 5 lines 9-16). If
the conjunction π‘1 , Β· Β· Β· , π‘π , with π = |π΅π |, is true w.r.t. πΌ βͺ β¬ (i.e. all positive literals are in πΌ
and all atoms appearing in some negative literals are not in β¬, Alg. 5 line 10) then π π‘πππ‘ππ is
derived as true, otherwise a flag variable stating that a ground rule with π π‘πππ‘ππ as head and an
undefined body w.r.t. πΌ βͺ β¬ exists is enabled (Alg. 5 line 15). Then, nested blocksβ scopes are
closed (Alg. 5 lines 17-22). After evaluating all switch-cases, if neither π π‘πππ‘ππ has not been
derived as true nor the π’ππππ flag is true then π π‘πππ‘ππ is derived as false (Alg. 5 lines 23-27).
Example of Compilation. In order to help understanding the outcome of our compiler, an
example is described in the following. Consider the following program Ξ :
π1 : π(π ) β π(π, π ), π(π, π), πππ‘ π(π)
π2 : π(π) β π (π), πππ‘ π(π)
π3 : π(π) β π(π), πππ‘ π(π)
The SCCs of πΊΞ are πΆ0 = {π}, πΆ1 = {π}, πΆ2 = {π}, πΆ3 = {π }, πΆ4 = {π}, πΆ5 = {π, π}.
Since for components πΆπ , with π from 0 to 4, there are no rules the code produced for them is
empty and so let us focus on πΆ5 . In this case, ππΆ5 is the entire program so Algorithm 1 prints a
sub-procedure for each rule of Ξ . Algorithm 6 reports the code produced by Algorithm 2 for
rule π1.
For evaluating π΅π1 , Algorithm 3 generates an external for-loop that iterates over ground
literals π‘1 that are not false w.r.t. πΌ βͺ β¬ and match π(π, π ) (Alg. 6 lines 4-35). For every π‘1 ,
the variable substitution π is updated mapping π and π to the first and the second term of π‘1
respectively (Alg. 6 lines 6-7). Nested into this for-loop, another for-loop is printed to iterate
over ground literals π‘2 matching π(π(π, π)) (Alg. 6 lines 9-33). Note that the application of
π to a literal will replace mapped variables with the value they are mapped to (i.e. π β¦β 1
then π(π(π, π)) = π(1, π)). Inside this for-loop π is updated by mapping π to the second term
of π‘2 (Alg. 6 lines 11-12). Then, the last literal to evaluate is πππ‘ π(π) and so, since the value
of π is already fixed by π‘2 then, the last nested block is an if-statement that checks whether
Algorithm 5 compileRulesForSupportDerivation
Input : A set of rules ππππ’ππ ππ£ππ
π’πππ , a set of predicates πΆ
Output : Prints a procedure that search for rule body that can support a given atom
1 begin
2 for all π β ππππ’ππ ππ£ππ
π’πππ do
3 βͺ case Jππππ(π»π )K β«
4 βͺ π = πβ«
5 for all π β 1, ..., |π‘ππ(π»π )| do
6 if π‘ππ(π»π )[π] is variable then
7 βͺ π = π βͺ { Jπ‘ππ(π»π )[π]K β¦β π‘ππ(π π‘πππ‘ππ)[ JπK ]}β«
8 ππππππππ
π’πππ΅πππ¦(π΅π , πΆ)
9 βͺ β = π( JπK )β«
10 βͺ if π+ β πΌ β§ (Β¬πβ β© β¬) = β
β§ β β / πΌ thenβ«
11 βͺ πΌ = πΌ βͺ {β}β«
12 βͺ β¬ = β¬ β {β}β«
13 βͺ π‘ππ’π = ππππ = β€β«
14 βͺ else if ((π+ βͺ Β¬πβ ) β© β¬) ΜΈ= β
β§ β β
/ πΌ thenβ«
15 βͺ π’ππππ = β€β«
16 βͺ fiβ«
17 for all π β [|π΅π |, . . . , 1] do
18 if π΅π [π] is positive literal then
19 βͺ π = π JπK β«
20 βͺdoneβ«
21 else
22 βͺfiβ«
23 βͺ if π’ππππ = β₯ β§ π‘ππ’π = β₯ thenβ«
24 βͺ β¬ = β¬ β {π π‘πππ‘ππ}β«
25 βͺ πΉ = πΉ βͺ {π π‘πππ‘ππ}β«
26 βͺ ππππ = β€β«
27 βͺ fiβ«
π‘3 = π(π(π)) is not true w.r.t. πΌ (Alg. 6 lines 14-31). At this point the conjunction π‘1 , π‘2 , π‘3
is an instantiation of π΅π and the generated code should derive the π(π ) (Alg. 6 lines 20-30).
Thus, if the body is true w.r.t. πΌ βͺ β¬ then β = π(π(π )) is added to πΌ, otherwise it is added
to β¬. Then, Algorithm 1 produces other two analogues procedures also for π2 and π3. Since
in this case, recursive rules are π2 and π3, but no positive literals have predicates in πΆ5 then
no switch-cases are generated at all and so the while-loop over π_π π‘πππ can be omitted. Thus,
Algorithm 1 continues by printing the last derivation scenario as previously described. In
particular, Algorithm 7 reports the switch-cases generated by Algorithm 5 for rules π2 and
π3 w.r.t. the component πΆ5 . The first switch-case refers to rule π2 (Alg. 7 lines 2-24) and so
it contains the code for evaluating π2 starting from a literal matching the π(π). Thus, π is
initialized by mapping π to the first term of π π‘πππ‘ππ literal (Alg. 7 lines 3-4), and then the
nested blocks are generated according to π΅π2 (Alg. 7 lines 5-13). Inside the last block, the head
derivation code has been printed (Alg. 7 lines 14-21). If the body instantiation π‘1 , π‘2 is true w.r.t.
πΌ βͺ β¬ then β = π π‘πππ‘ππ is derived as true, otherwise the π’ππππ flag is enabled. The second
switch-case is analogous to the first one but it refers to π3 and so the evaluation starts from a
literal, π π‘πππ‘ππ, matching the atom π(π). Out of the scope of the switch-statement if no rule in-
stantiations have been founded for the atom π π‘πππ‘ππ then it is derived as false (Alg. 7 lines 48-51).
Algorithm 6 Output example compileRuleForHeadDerivation
Input : {π(π, π ), π(π, π), πππ‘ π(π)}, π(π ), {β² πβ² ,β² π β² }
1 begin
2 π=π
3 π1 = π
4 π1 = {π β (πΌ βͺ β¬) | πππ‘πβ(π(π(π, π )), π)}
5 for all π‘1 β π1 do
6 π = π βͺ {π β¦β π‘ππ(π‘1 )[1]}
7 π = π βͺ {π β¦β π‘ππ(π‘1 )[2]}
8 π2 = π
9 π2 = {π β (πΌ βͺ β¬) | πππ‘πβ(π(π(π, π)), π)}
10 for all π‘2 β π2 do
11 π = π βͺ {π β¦β π‘ππ(π‘2 )[1]}
12 π = π βͺ {π β¦β π‘ππ(π‘2 )[2]}
13 π3 = π
14 π‘3 = π(πππ‘ π(π))
15 if π‘3 β/ πΌ then
16 π = ππ = β
17 π = π βͺ {π‘1 }
18 π = π βͺ {π‘2 }
19 π = π βͺ {π‘3 }
20 β = π(π(π ))
21 if ππ == β
β§ π+ β πΌ β§ (Β¬πβ β© β¬) = β
then
22 π_π π‘πππ = π_π π‘πππ βͺ {β}
23 πΌ = πΌ βͺ {β}
24 β¬ = β¬ β {β}
25 else
26 if β β
/ (πΌ βͺ β¬)
27 π_π π‘πππ = π_π π‘πππ βͺ {β}
28 β¬ = β¬ βͺ {β}
29 fi
30 fi
31 fi
32 π = π2
33 done
34 π = π1
35 done
Algorithm 7 Output example compileRulesForSupportDerivation
Input : {π(π) β π(π), πππ‘ π(π); π(π) β π (π), πππ‘ π(π)}, {β² πβ² ,β² π β² }
1 begin
2 case βaβ
3 π=π
4 π = π βͺ {π β¦β π‘ππ(π π‘πππ‘ππ)[1]}
5 π1 = π
6 π1 = {π β (πΌ βͺ β¬) | πππ‘πβ(π(π (π)), π)}
7 for all π‘1 β π1 do
8 π = π βͺ {π β¦β π‘ππ(π‘1 )[1]}
9 π‘2 = π(πππ‘ π(π))
10 if π‘2 β/ πΌ then
11 π = ππ = β
12 π = π βͺ {π‘1 } π = π βͺ {π‘2 }
13 ππ = ππ βͺ {π‘2 }
14 β = π π‘πππ‘ππ
15 if π+ β πΌ β§ (Β¬πβ β© β¬) = β
β§ β β / πΌ then
16 πΌ = πΌ βͺ {β}
17 β¬ = β¬ β {β}
18 π‘ππ’π = ππππ = β€
19 else if ((π+ βͺ Β¬πβ ) β© β¬) ΜΈ= β
β§ β β
/ πΌ then
20 π’ππππ = β€
21 fi
22 fi
23 π = π1
24 done
25 case βgβ
26 π=π
27 π = π βͺ {π β¦β π‘ππ(π π‘πππ‘ππ)[1]}
28 π1 = π
29 π1 = {π β (πΌ βͺ β¬) | πππ‘πβ(π(π(π)), π)}
30 for all π‘1 β π1 do
31 π = π βͺ {π β¦β π‘ππ(π‘1 )[1]}
32 π‘2 = π(πππ‘ π(π))
33 if π‘2 β/ πΌ then
34 π = ππ = β
35 π = π βͺ {π‘1 } π = π βͺ {π‘2 }
36 ππ = ππ βͺ {π‘2 }
37 β = π π‘πππ‘ππ
38 if π+ β πΌ β§ (Β¬πβ β© β¬) = β
β§ β β / πΌ then
39 πΌ = πΌ βͺ {β}
40 β¬ = β¬ β {β}
41 π‘ππ’π = ππππ = β€
42 else if ((π+ βͺ Β¬πβ ) β© β¬) ΜΈ= β
β§ β β
/ πΌ then
43 π’ππππ = β€
44 fi
45 fi
46 π = π1
47 done
48 if π’ππππ = β₯ β§ π‘ππ’π = β₯ then
49 β¬ = β¬ β {π π‘πππ‘ππ}
50 ππππ = β€
51 fi
4. Implementation and Experiments
Implementation Details. The compilation strategy described in the previous section has
been entirely implemented in C++ and so both compiler and generated procedures are written
in C++. Generated code is built on top of optimized data structures that allow to speed up the
whole computation process. In particular, it uses a numerical representation of constant terms
that allows a compact and uniform representation. Moreover, different indexing structures
are used for each predicate. Indexes are defined on the subset of terms of predicates, for fast
retrieval of the list of literals that match a possible tuple.
Benchmarks, Systems and Experiments Setup. In order to assess the performances of
the proposed approach we conducted an empirical evaluation both on positive programs (i.e.,
Datalog) and programs with negation. Among positive programs, we considered:
β’ Large-join problem [11] defined as follows:
π(π, π ) β π1(π, π), π2(π, π ).
π1(π, π ) β π1(π, π), π2(π, π ).
π2(π, π ) β π3(π, π), π4(π, π ).
π1(π, π ) β π1(π, π), π2(π, π ).
where π1/2, π2/2, π2/2, π3/2, and π4/2 are defined as facts that represent an instance
of the problem. For this benchmark, we have generated instances of different sizes in
a random fashion. More precisely, for each instance, we set the size (number of facts)
roughly from 10000 to 10000000 and we randomly divided the number of facts among the
previous predicates (around 10-25% for each predicate). Then for each predicate π, we
randomly estimated the max value for each term, π and π, in such a way that π * π = π ,
where π is the size of the predicate set of π.
β’ Reachability problem defined as follows:
ππππβ(π, π ) β ππππ(π, π ).
ππππβ(π, π ) β ππππβ(π, π), ππππ(π, π ).
Instances of this problem are directed graphs that we have generated varying the number
of nodes and the density of the edges. In particular, we considered graphs with a number
of nodes from 100 to 2000 and density 20, 40, 60, 80, and 100%.
Among programs with negation, instead, we considered three hard benchmarks from asp com-
petitions that are Knight Tour with Holes, Stable Marriage, and Graph Colouring [10].
Our approach, labeled wf-comp, was compared with the following tools:
β’ General-purpose systems that can evaluate Datalog programs: idlv [13] and gringo [14]
β’ The soufflΓ© framework [4] for which we have rewritten the encoding to produce a suitable
encoding for this framework. In particular, two versions have been considered, the
interpreted, souffle, and compiled, souffle-comp, ones.
β’ Compilation-based approach for stratified normal programs wasp-lazy [7].
1,800
souffle
souffle-comp
1,500
gringo
idlv
Execution Time (s)
1,200 wasp-lazy
wf-comp
900
600
300
0
0 10 20 30 40 50 60
Number of solved instances
Figure 1: Systems comparison on large-join domain
1,800
souffle
souffle-comp
1,500
gringo
idlv
Execution Time (s)
1,200 wasp-lazy
wf-comp
900
600
300
0
0 20 40 60 80 100
Number of solved instances
Figure 2: Systems comparison on reachability domain
β’ ASP system dlv2 [12] that can evaluate normal programs under well-founded semantics.
All the experiments were executed on a machine equipped with Xeon(R) Gold 5118 CPUs,
running Ubuntu Linux (kernel 5.4.0-77-generic). Time and memory were limited to 1800
seconds and 8GB, respectively. Source code and benchmark suite are available at https://osf.io/
g9n2z/?view_only=aeb0777c469e46499247284b56dfb598
Evaluation on Positive Programs. In this comparison, we run the systems on instances of
large-join and reachability. Obtained results are summarized by the cactus plots in Figures 1-2.
10
dlv2
wf-comp
8
Execution Time (s)
6
4
2
0
0 50 100 150 200 250 300 350
Number of solved instances
Figure 3: Systems comparison on Knight Tour With Holes benchmark
10
dlv2
wf-comp
8
Execution Time (s)
6
4
2
0
0 15 30 45 60 75
Number of solved instances
Figure 4: Systems comparison on Stable Marriage benchmark
Recall that a cactus plot reports a line for each system and each line contains a point (π, π ) if
a given system is able to solve π within a time limit of π seconds.
In both cases, wf-comp outperforms state-of-the-art ASP systems gringo and idlv solving
more instances (roughly 15 for reachability and 2 for large-join) and in less time overall. Our
approach also outperforms wasp-lazy on reachability problem solving 16 more instances than
the latter, while wf-comp is comparable on the large-join domain with wasp-lazy. The best
method in this comparison is souffle system. The difference with our tool (souffle is preferable
in complete graphs) is due to different data structures and also to the different input formats.
Indeed, souffle takes as input a numeric format where input facts are organized in files for
35
dlv2
30 wf-comp
25
Execution Time (s)
20
15
10
5
0
0 40 80 120 160
Number of solved instances
Figure 5: Systems comparison on Graph Colouring benchmark
each input predicate while wf-comp reads plain text files.
Evaluation on Programs with Negation. In the case of programs with negation, we com-
pare wf-comp and dlv2 (the other methods do not support unrestricted negation). For each
considered benchmark we report a cactus plot see Figures 3, 4, and 5. Obtained results highlight
the strength of the proposed approach that outperforms dlv2 on considered benchmarks. In
particular, both systems are able to solve all problem instances within time and memory limits,
but wf-comp significantly reduced the total execution time for each benchmark (33.24% on
Graph Colouring, 63.26% on Knight Tour With Holes, and 22.37% on Stable Marriage).
5. Conclusion
Logic programming is a widely employed programming paradigm for modeling complex
problems in a declarative fashion. Efficient implementations are needed in order to exploit
the strength of such formalism in real-world applications. Compilation-based techniques
were revealed to be effective in tackling different issues raised in the evaluation of logic pro-
grams [4, 7, 6, 8]. In this paper, we proposed a compilation-based approach for logic programs
under well-founded semantics. Obtained results demonstrate the effectiveness of the proposed
approach both for positive programs and programs with negation. The improvements are
significant on programs with negation while in the evaluation of positive programs our imple-
mentation is competitive with existing systems. As future works, we planned to extend our
technique also to the class of disjunctive programs and to programs with aggregates (again
under the well-founded semantics). Also, there is space for improvements in the data structures
used for evaluating Datalog programs, where better performance could possibly be achieved
(especially on dense graphs) by employing more efficient indexing structures.
References
[1] J. W. Lloyd, Foundations of Logic Programming, 2nd Edition, Springer, 1987.
[2] C. Baral, M. Gelfond, Logic programming and knowledge representation, The Journal of
Logic Programming 19 (1994) 73β148.
[3] E. Dantsin, T. Eiter, G. Gottlob, A. Voronkov, Complexity and expressive power of logic
programming, ACM Comput. Surv. 33 (2001) 374β425.
[4] H. Jordan, B. Scholz, P. Subotic, SoufflΓ©: On synthesis of program analyzers, in:
S. Chaudhuri, A. Farzan (Eds.), Computer Aided Verification - 28th International Con-
ference, CAV 2016, Toronto, ON, Canada, July 17-23, 2016, Proceedings, Part II, vol-
ume 9780 of Lecture Notes in Computer Science, Springer, 2016, pp. 422β430. URL: https:
//doi.org/10.1007/978-3-319-41540-6_23.
[5] B. Cuteri, C. Dodaro, F. Ricca, P. SchΓΌller, Partial compilation of ASP programs, Theory
Pract. Log. Program. 19 (2019) 857β873. URL: https://doi.org/10.1017/S1471068419000231.
[6] B. Cuteri, C. Dodaro, F. Ricca, P. SchΓΌller, Overcoming the grounding bottleneck due
to constraints in ASP solving: Constraints become propagators, in: C. Bessiere (Ed.),
Proceedings of the Twenty-Ninth International Joint Conference on Artificial Intelligence,
IJCAI 2020, ijcai.org, 2020, pp. 1688β1694. URL: https://doi.org/10.24963/ijcai.2020/234.
[7] B. Cuteri, F. Ricca, A compiler for stratified datalog programs: preliminary results, in:
S. Flesca, S. Greco, E. Masciari, D. SaccΓ (Eds.), Proceedings of the 25th Italian Symposium
on Advanced Database Systems, Squillace Lido (Catanzaro), Italy, June 25-29, 2017, volume
2037 of CEUR Workshop Proceedings, CEUR-WS.org, 2017, p. 158. URL: http://ceur-ws.org/
Vol-2037/paper_23.pdf.
[8] G. Mazzotta, F. Ricca, C. Dodaro, Compilation of aggregates in ASP systems, in: AAAI,
AAAI Press, 2022, pp. 5834β5841.
[9] A. Van Gelder, K. A. Ross, J. S. Schlipf, The well-founded semantics for general logic
programs, J. ACM 38 (1991) 620β650. URL: https://doi.org/10.1145/116825.116838.
[10] M. Gebser, M. Maratea, F. Ricca, The sixth answer set programming competition, Journal
of Artificial Intelligence Research 60 (2017) 41β95.
[11] S. Liang, P. Fodor, H. Wan, M. Kifer, Openrulebench: an analysis of the performance of
rule engines, in: J. Quemada, G. LeΓ³n, Y. S. Maarek, W. Nejdl (Eds.), Proceedings of the
18th International Conference on World Wide Web, WWW 2009, Madrid, Spain, April
20-24, 2009, ACM, 2009, pp. 601β610. URL: https://doi.org/10.1145/1526709.1526790.
[12] M. Alviano, F. Calimeri, C. Dodaro, D. FuscΓ , N. Leone, S. Perri, F. Ricca, P. Veltri, J. Zangari,
The ASP system DLV2, in: Logic Programming and Nonmonotonic Reasoning - 14th
International Conference, LPNMR 2017, Espoo, Finland, July 3-6, 2017, Proceedings, volume
10377 of Lecture Notes in Computer Science, 2017, pp. 215β221. URL: https://doi.org/10.1007/
978-3-319-61660-5_19.
[13] F. Calimeri, D. FuscΓ , S. Perri, J. Zangari, I-DLV: the new intelligent grounder of DLV,
Intelligenza Artificiale 11 (2017) 5β20. URL: https://doi.org/10.3233/IA-170104.
[14] M. Gebser, R. Kaminski, A. KΓΆnig, T. Schaub, Advances in gringo series 3, in: J. P. Delgrande,
W. Faber (Eds.), Logic Programming and Nonmonotonic Reasoning - 11th International
Conference, LPNMR 2011, Vancouver, Canada, May 16-19, 2011. Proceedings, volume 6645
of Lecture Notes in Computer Science, 2011, pp. 345β351.