[

[

[
Abstract

Termination is an important and well-studied property for logic programs. However, almost all approaches for automated termination analysis focus on definite logic programs, whereas real-world Prolog programs typically use the cut operator. We introduce a novel pre-processing method which automatically transforms Prolog programs into logic programs without cuts, where termination of the cut-free program implies termination of the original program. Hence after this pre-processing, any technique for proving termination of definite logic programs can be applied. We implemented this pre-processing in our termination prover AProVE and evaluated it successfully with extensive experiments.

Automated Termination Analysis for Logic Programs with Cut]Automated Termination Analysis for Logic Programs with Cutthanks: Supported by the Deutsche Forschungsgemeinschaft (DFG) under grant GI 274/5-2, the DFG Research Training Group 1298 (AlgoSyn), and the Danish Natural Science Research Council. P. Schneider-Kamp et al.]PETER SCHNEIDER-KAMP
Dept. of Mathematics and Computer Science, University of Southern Denmark, Denmark and JÜRGEN GIESL, THOMAS STRÖDER
LuFG Informatik 2, RWTH Aachen University, Germany and ALEXANDER SEREBRENIK
Dept. of Mathematics and Computer Science, TU Eindhoven, The Netherlands and RENÉ THIEMANN
Institute of Computer Science, University of Innsbruck, Austria

KEYWORDS: automated termination analysis, cut, definite logic programs  

1 Introduction

Automated termination analysis for logic programs has been widely studied, see,e.g., ([Bruynooghe et al. 2007]; [Codish et al. 2005]; [De Schreye and Decorte 1994];[Mesnard and Serebrenik 2007]; [Nguyen et al. 2010]; [Schneider-Kamp et al. 2009];[Serebrenik and De Schreye 2005]). Still, virtually all existing techniques only prove universal termination of definite logic programs, which do not use the cut “!”. An exception is [Marchiori 1996], which transforms “safely typed” logic programs to term rewrite systems (TRSs). However, the resulting TRSs are complex and since there is no implementation of [Marchiori 1996], it is unclear whether they can be handled by existing TRS termination tools. Moreover, [Marchiori 1996]’s method does not allow arbitrary cuts (e.g., it does not operate on programs like Ex. 1).

In the present paper, we introduce a novel approach which shows that universal termination of logic programs with cuts can indeed be proved automatically for (typically infinite) classes of queries. This solves an important open problem in automated termination analysis of logic programs.

Example 1

We want to prove termination of the following program for the class of queries . Since we only regard programs without pre-defined predicates, the program contains clauses defining predicates for failure and equality. So the atom always fails and corresponds to Prolog’s pre-defined “fail”.

(1)
(2)
(3)
(4)
(5)
(6)
(7)
(8)

Any termination analyzer that ignores the cut fails, as would lead to the subtraction of and start an infinite derivation using Clause (1). So due to the cut, (universal) termination effectively depends on the order of the clauses.

There are already several static analysis techniques for logic programming with cut, e.g., [Filé and Rossi 1993, Mogensen 1996], which are based on abstract interpretation [Cousot and Cousot 1992, Le Charlier et al. 1994, Spoto and Levi 1998]. However, these works do not capture termination as an observable and none of these results targets termination analysis explicitly. While we also rely on the idea of abstraction, our approach does not operate directly on the abstraction. Instead, we synthesize a cut-free logic program from the abstraction, such that termination of the derived program implies termination of the original one. Thus, we can benefit from the large body of existing work on termination analysis for cut-free programs. Our approach is inspired by our previous successful technique for termination analysis of Haskell programs [Giesl et al. 2006], which in turn was inspired by related approaches to program optimization [Sørensen and Glück 1995].

In Sect. 2, we introduce the required notions and present a set of simple inference rules that characterize logic programming with cut for concrete queries. In Sect. 3 we extend these inference rules to handle classes of queries. Using these rules we can automatically build so-called termination graphs, cf. Sect. 4. Then, Sect. 5 shows how to generate a new cut-free logic program from such a graph automatically.

Of course, one can transform any Turing-complete formalism like logic programming with cuts into another Turing-complete formalism like cut-free logic programming. But the challenge is to develop a transformation such that termination of the resulting programs is easy to analyze by existing termination tools. Our implementation and extensive experiments in Sect. 6 show that with our approach, the resulting cut-free program is usually easy to handle by existing tools.

2 Concrete Derivations

See e.g. [Apt 1997] for the basics of logic programming. We distinguish between individual cuts to make their scope explicit. So a signature contains all predicate and function symbols and all labeled versions of the cut . For simplicity we just consider terms and no atoms, i.e., we do not distinguish between predicate and function symbols. To ease the presentation, in the paper we exclude terms with cuts as proper subterms. A clause is a pair where the head is from and the body is a sequence of terms from . Let be the set of all such sequences, where is the empty goal.

A program (possibly with cut) is a finite sequence of clauses. are all clauses for ’s predicate, i.e., .

A substitution is a function and we often denote its application to a term by instead of . As usual, and . The restriction of to is if , and otherwise. A substitution is the most general unifier (mgu) of and iff and, whenever for some , there exists a such that for all . If and have no mgu, we write . Finally, to denote the term resulting from replacing all occurrences of a function symbol in a term by another function symbol , we write .

Now we recapitulate the operational semantics of logic programming with cut. Compared to other formulations like [Andrews 2003, Billaud 1990, de Vink 1989, Kulas and Beierle 2000, Spoto 2000], the advantage of our formalization is that it is particularly suitable for an extension to classes of queries in Sect. 3 and 4, and for synthesizing cut-free programs in Sect. 5. A formal proof on the correspondence of our inference rules to the semantics of the Prolog ISO standard [Deransart et al. 1996] can be found in [Ströder 2010].

Our semantics is given by 7 inference rules. They operate on states which represent the current goal, and also the backtrack information that is needed to describe the effect of cuts. The backtrack information is given by a sequence of goals which are optionally labeled by the program clause that has to be applied to the goal next. Moreover, our states also contain explicit marks for the scope of a cut.

Definition 1 (Concrete State)

A concrete state is a sequence of elements from , where elements are separated by “”. is the set of all states.

So an element of a state can be ; or a labeled goal representing that we must apply the -th program clause to next, where determines how a cut introduced by the -th clause will be labeled; or . Here, serves as a marker to denote the end of the scope of cuts labeled with . Whenever a cut is reached, all elements preceding are discarded.

Now we express derivations in logic programming with cut by seven rules. Here, and are concrete states and the goal may also be (then “” is ).

Definition 2 (Semantics with Concrete Inference Rules)

The Suc rule is applicable if the first goal of our sequence could be proved. As we handle universal termination, we then have to backtrack to the next goal in the sequence. Fail means that for the current -th case analysis, there are no further backtracking possibilities. But the whole derivation does not have to fail, since the state may still contain further alternative goals which have to be examined.

To make the backtracking possibilities explicit, the resolution of a program clause with the first atom of the current goal is split into two operations. The Case analysis determines which clauses could be applied to by slicing the program according to ’s root symbol. It replaces the current goal by a goal labeled with the index of the first such clause and adds copies of labeled by the indices of the other potentially applicable clauses as backtracking possibilities. Note that here, the top-down clause selection rule is taken into account. Additionally, these goals are labeled by a fresh mark that is greater than all previous marks, and is added at the end of the new backtracking goals to denote

Case

Eval

Cut

Case

Backtrack

Fail

Fail

the scope of cuts. For instance, consider the program of Ex. 1 and the query . Here, we obtain the sequence depicted at the side. The Case rule results in a state which represents a case analysis where we first try to apply the first -clause (1). When backtracking later on, we use clauses (1) and (1).

For a goal , if unifies with the head of the corresponding clause, we apply Eval. This rule replaces by the body of the clause and applies the mgu to the result. When depicting rule applications as trees, the corresponding edge is labeled with . All cuts occurring in are labeled with . The reason is that if one reaches such a cut, then all further alternative goals up to are discarded.

If does not unify with , we apply the Backtrack rule. Then, Clause cannot be used and we just backtrack to the next possibility in our backtracking sequence.

Finally, there are two Cut rules. The first rule removes all backtracking information on the level where the cut was introduced. Since the explicit scope is represented by and , we have turned the cut into a local operation depending solely on the current state. Note that must not be deleted as the current goal could still lead to another cut . The second Cut rule is used if is missing (e.g., if a cut is already in the initial query). Later on, such states can also result from the additional Parallel inference rule which will be introduced in Sect. 4. We treat such states as if were added at the end of the backtracking sequence.

Note that these rules do not overlap, i.e., there is at most one rule that can be applied to any state. The only case where no rule is applicable is when the state is the empty sequence (denoted ) or when the first goal starts with a variable.

The rules of Def. 2 define the semantics of logic programs with cut using states. They can also be used to define the semantics using derivations between goals: there is a derivation from the goal to in the program (denoted ) iff repeated application of our rules can transform the state111If contains cuts, then the inference rules have to be applied to instead of . to a state of the form for some , and results from by removing all labels. Moreover, where are the mgu’s used in those applications of the Eval rule that led to . We call the corresponding answer substitution. If is not of interest, we write instead of .

Consequently, our inference rules can be used for termination proofs: If there is an infinite derivation (w.r.t. ) starting in some goal , then there is also an infinite sequence of inference rule applications starting in the state , i.e., is a “non-terminating state”. Note that we distinguish derivations in logic programming (i.e., for goals and ) from sequences of states that result from application of the inference rules in Def. 2. If a state can be transformed into a state by such an inference rule, we speak of a “state-derivation”.

3 Abstract Derivations

To represent classes of queries, we introduce abstract terms and a set of abstract variables, where each represents a fixed but arbitrary term. consists of all “ordinary” variables in logic programming. Then, as abstract terms we consider all terms from the set where . Concrete terms are terms from , i.e., terms containing no abstract variables. For any set , let be the variables from occurring in the term .

To determine by which terms an abstract variable may be instantiated, we add a knowledge base to each state, where and . The variables in may only be instantiated by ground terms. And means that we are restricted to instantiations of the abstract variables where , i.e., and may not become unifiable when instantiating them with .

Definition 3 (Abstract State)

The set of abstract states is a set of pairs of a concrete state and a knowledge base .

A substitution is a concretization of an abstract state if it respects the knowledge base . So first, instantiates all abstract variables, i.e., . Second, when applying , the resulting term must be concrete, i.e., . Third, abstract variables from may only be replaced by ground terms, i.e., . Fourth, for all pairs , and must not unify.

Definition 4 (Concretization)

A substitution is a concretization w.r.t.  iff , , , and for all . The set of concretizations of an abstract state is .

Example 2

Consider the abstract state which consists of the single goal and the knowledge base , with for all . So here and only contains . This represents all concrete states where are ground terms and where and do not unify, i.e., does not match . For example, is not represented as and unify. In contrast, and are represented. Note that can be reduced to using Clause (1) from Ex. 1. But Clause (1) cannot be applied to all concretizations. For example, the concrete state is also represented by our abstract state, but here no clause is applicable.

Ex. 2 demonstrates that we need to adapt our inference rules to reflect that sometimes a clause can be applied only for some concretizations of the abstract variables, and to exploit the information from the knowledge base of the abstract state. We now adapt our inference rules to abstract states that represent sets of concrete states. The invariant of our rules is that all states represented by the parent node are terminating if all the states represented by its children are terminating.

Definition 5 (Sound Rules)

An abstract state is called terminating iff all its concretizations are terminating. A rule is sound if is terminating whenever all are terminating.

The rules Suc, Fail, Cut, and Case do not change the knowledge base and are, thus, straightforward to adapt. Here, stands for .

Definition 6 (Abstract Inference Rules – Part 1 (Suc, Fail, Cut, Case))

In Def. 2, we determined which of the rules Eval and Backtrack to apply by trying to unify the first atom with the head of the corresponding clause. But as demonstrated by Ex. 2, in the abstract case we might need to apply Eval for some concretizations and Backtrack for others. Backtrack can be used for all concretizations if does not unify with or if their mgu contradicts . This gives rise to the abstract Backtrack rule in the following definition. When the abstract Backtrack rule is not applicable, we still cannot be sure that unifies with for all concretizations . Thus, we have an abstract Eval rule with two successor states that combines both the concrete Eval and the concrete Backtrack rule.

Definition 7 (Abstract Inference Rules – Part 2 (Backtrack, Eval))

where and . W.l.o.g., only contains fresh abstract variables for all . Moreover, and .

In Eval, w.l.o.g. we assume that renames all variables to fresh abstract variables. This is needed to handle “sharing” effects correctly, i.e., to handle concretizations which introduce multiple occurrences of (concrete) variables, cf. [Schneider-Kamp et al. 2010]. The knowledge base is updated differently for the successors corresponding to the concrete Eval and Backtrack rule. For all concretizations corresponding to the second successor of Eval, the concretization of does not unify with . Hence, here we add the pair to the set .

Now consider concretizations where and unify, i.e., concretizations corresponding to the first successor of the Eval rule. Then for any , is a ground instance of . Hence, we replace all by , i.e., we apply to and . Now the new set of abstract variables that may only be instantiated by ground terms is . As before, is replaced by the instantiated clause body where we label cuts with the number of the current Case analysis.

Now any concrete derivation with the rules from Def. 2 can also be simulated with the abstract rules from Def. 6 and 7. But unfortunately, even for terminating goals, in general these rules yield an infinite tree. The reason is that there is no bound on the size of terms represented by the abstract variables and hence,

Case

Eval

Fail

Eval

Case

Eval

Eval

Case

Fail

the abstract Eval rule can be applied infinitely often.

Example 3

Consider the 1-rule program

(9)

For queries of the form where is ground, the program terminates. However, the tree built using the abstract inference rules is obviously infinite.

4 From Trees to Graphs

To obtain a finite graph instead of an infinite tree, we now introduce an additional Instance rule which allows us to connect the current state with a previous state , provided that the current state is an instance of the previous state. In other words, every concretization of must be a concretization of . Still, Instance is often not enough to obtain a finite graph.

Example 4

We extend Ex. 3 by the following additional fact.

(10)

For queries where is ground, the program still terminates. If we start with , then the Case rule results in the state and the Eval rule produces two new states, one of them being .

To simplify states, from now on we will eliminate so-called non-active marks which occur as first or as last element in states. Eliminating from the beginning of a state is possible, as Fail would also remove such a . Eliminating from the end of a state is possible, as applying the first Cut rule to a state ending in is equivalent to applying the second Cut rule to the same state without .

We will also reduce the knowledge base to just those abstract variables that occur in the state and remove pairs from where . Still,

Case

Parallel

Parallel

Eval

Instance

Eval

Eval

Eval

Suc

is not an instance of the previous state due to the ad-ded backtrack goal . Therefore, we now introduce a Parallel rule that allows us to split a backtracking sequence into separate problems. Now we obtain the graph on the right.

Clearly, Parallel may transform terminating into non-terminating states. But without further conditions, Parallel is not only “incomplete”, but also unsound. Consider a state for the program . The state is not terminating, as is not reachable. Thus, one eventually evaluates . But if one splits the state into and , both new states terminate.

To solve this problem, in addition to the “active marks” (cf. Ex. 4) we introduce the notion of active cuts. The active cuts of a state are those where occurs in or where can be introduced by Eval applied to a labeled goal occurring in . Now the Parallel rule may only split a backtracking sequence into two parts and if the active cuts of and the active marks of are disjoint.

Definition 8 (Abstract Inference Rules – Part 3 (Instance, Parallel))

The active cuts are all where is in or is in and ’s body has a cut. The active marks are all where and .

Case

Eval

Eval

Case

Eval

Eval

Case

Example 5

However, there are still examples where the graph cannot be “closed”. Consider the program

(11)
(12)

For queries where is ground, the program again terminates. With Def. 6, 7, and 8, we obtain the infinite tree on the right. It never encounters an instance of a previous state, since each resolution with Clause (11) adds a to the goal.

Thus, we introduce a final abstract Split rule to split a state into and a state , where approximates the answer substitutions for . The edge from to is labeled with . To simplify the Split rule, we only define it for backtracking sequences of one element. To obtain such a sequence, we can use the Parallel rule.

Definition 9 (Abstract Inference Rules – Part 4 (Split))

Here, is defined as follows. We assume that we have a groundness analysis function , see, e.g., [Howe and King 2003]. If is an -ary predicate, , and , then any successful derivation where are ground will lead to an answer substitution such that are ground. So approximates which positions of will become ground if the “input” positions are ground. Now if is an abstract term where are ground in every concretization (i.e., all their variables are from ), then returns the -renamings of all abstract variables that will be ground in every successful derivation starting from a concretization of . Thus, contains the abstract variables of . So formally

Example 6

To illustrate Def. 9, regard the program of Ex. 1 and the state with . (This state will occur in the termination proof of , cf. Ex. 7.) We have and hence if is , then . In other words, if the first two arguments of are ground and the derivation is successful, then the answer substitution also instantiates the third argument to a ground term. Since only renames variables outside of , we have . So . So the Split rule transforms the current state

Case

Eval

Eval

Split

Instance

Split

Case

Eval

Eval

Suc

to and where one can eliminate from the new groundness set .

With the additional Split rule, we can always obtain finite graphs instead of infinite trees. (This will be proved in Thm. 2.) Thus, no further rules are needed. As depicted on the right, now we can also close the graph for Ex. 5’s program.

Thm. 1 proves the soundness of all our abstract inference rules. In other words, if all children of a node are terminating, then the node is terminating as well.

Theorem 1 (Soundness of the Abstract Inference Rules)

The inference rules from Def. 6, 7, 8, and 9 are sound.222For all proofs, we refer to [Schneider-Kamp et al. 2010].

5 From Termination Graphs to Logic Programs

Now we introduce termination graphs as a subclass of the graphs obtained by Def. 6, 7, 8, 9. Then we show how to extract cut-free programs from termination graphs.

Definition 10 (Termination Graph)

A finite graph built from an initial state using Def. 6, 7, 8, and 9 is a termination graph iff there is no cycle consisting only of Instance edges and all leaves are of the form or with . If there are no leaves of the form , then the graph is “proper”.

We want to generate clauses for the loops in the termination graph and show their termination. Thus, there should be no cycles consisting only of Instance edges, asthey would lead to trivially non-terminating clauses. Moreover, the only leaves may be nodes where no inference rule is applicable anymore (i.e., the graph must be “fully expanded”). For example, the graph at the end of Sect. 4 is a termination graph. Thm. 2 shows that termination graphs can always be obtained automatically.

Theorem 2 (Existence of Termination Graphs)

For any program and abstract state , there exists a termination graph.

Example 7

For the program from Ex. 1 we obtain the termination graph below. Here, results from exploiting the cuts. implies that neither nor unify with . Thus, only Clause (1) is applicable to evaluate the state in Node d. This is crucial for termination, because in d, ’s result is always smaller than ’s input argument and therefore, ’s first argument in Node c is smaller than ’s first argument in Node a.

Remember that our goal is to show termination of the graph’s initial state. Since the graph only has leaves that are clearly terminating, by soundness of theinference rules, it remains to prove that there is no state-derivation corresponding to an infinite traversal of the cycles in the graph. So in our example, we have to show that the Instance edges for and cannot be traversed infinitely often.

a

Case

Eval

Cut

Case

Eval

Eval

Cut

Case

b

Eval

Suc

Eval

Eval

Eval

Eval

d

Split

c

Split

Instance

Case

Backtrack

Backtrack

e

Eval

Eval

Case

Parallel

Parallel

f

Eval