• No results found

We analyse int-TRSs as defined in Def. 2.28. To prove their termination, we structure our proofs as proof trees, where each node is labelled by an int-TRS. In our framework, we iteratively apply a series of processors on the leaves of the proof tree, and each application either yields a sequence of new, modified (and usually simplified) int-TRSs or a result in the form of yes (for a terminating system) or no (for a non-terminating system). We can then propagate these results upwards in the proof tree. For this, each applied processor is marked as being either sound (if the termination of all resulting int-TRSs implies termination of R), or complete (if termination of R implies the termination of all of the resulting rewrite systems) or both.

Definition 3.1 (Processor) A processor P maps an int-TRSs R to a set {R1, . . . , Rn}

or one of the special values yes or no. We call P sound if the termination of all resulting int-TRSs implies termination of R and it only returns yes if R is terminating. We call

P complete if non-termination of one of the resulting int-TRSs implies non-termination

of R and it only returns no if R is non-terminating.

We build a proof tree for an input int-TRS R by starting with R as root and appending new children R1, . . . , Rn to a leaf Rl whenever we have a processor P with P(Rl) =

{R1, . . . , Rn}. Such a proof tree proves termination of its root R if all leaves are yes and

all applied processors are sound, and it proves non-termination of R if one of the leaves is labelled with no and all processors applied between the root and that leaf are complete.

Example 3.2 (Proof Tree for Terminating Example)

R1 R2 yes R3 yes Figure 3.1 Fig. 3.1 displays a proof tree for a terminating int-TRS R. For example,

we could have R2 = {f(x) → f(x − 1) J x > 0K}, R3 = {g(x) → g(x − 1) J x > 0K}, and R1 = R2 ∪ R3. Then, a simple processor allows us to consider R2 and R3 instead of R1, as the two different rules are obviously (syntactically) independent of each other (a sound and complete processor to do this will be presented in Thm. 3.5). For the resulting subsystems, we can automatically find simple termination

arguments to conclude their termination, and a corresponding processor will be defined in Thm. 3.20.

We will first present a simple processor to split a large int-TRS under analysis into several smaller ones, taken from [FKS11] (which in turn is an adaptation of [GTS04, GTSF06] to conditional integer rules). For this, we generalise the idea of the simple syntactical splitting

in Ex. 3.2 by building a rule graph from an int-TRS R.1 The nodes of a rule graph are the rules of R, and we connect two rules `1 → r1 J ϕ1K and `2 → r2 J ϕ2K by a directed edge if they can be used after each other. Intuitively, this matches the concept of control flow graphs in imperative programming languages, indicating how evaluation sequences may be composed from the definition of the program.

Definition 3.3 (Rule Graph) Let R be an int-TRS. The rule graph of R is the directed

graph RG(R) := (R, E) with

E := {(ρ1, ρ2) ∈ R2 | ∃t1, t2, t3 ∈ T (Σ, V).t1 ,→ρ1 t2 ,→ρ2 t3}

Of course, in the presence of non-linear arithmetic in the constraints of R, RG(R) may not be computable. However, in many cases (e.g., for linear arithmetic), and using approximations otherwise, we can compute the graph for two given rules using a simple technique. To check if `1 → r1 J ϕ1K and `2 → r2 J ϕ2K can be applied after each other, we first rename all variables to avoid name clashes. Then, we try to find the most general

unifier (mgu) σ for the terms r1 and `2, i.e., the most general substitution such that

r1σ = `2σ. If no such σ exists, the two rules cannot be applied after each other, because the constraints imposed by the respective terms are contradictory. So in the most simple case, we have rules . . . → f(. . .) and g(. . .) → . . . and we cannot find a unifier because f and g represent different “program positions”. We may also have rules . . . → f(List(. . .), . . .) and f(null, . . .) → . . ., where the first rule ensures that a list is non-empty and the second rule is only applicable if the list is empty.

On the other hand, if we succeed in finding a unifier σ, we have only ensured that the constraints on the term structure are satisfiable. To check that our integer constraints are satisfiable as well, we then check if ϕ1σ ∧ ϕ2σ is satisfiable. If that is not the case, the rules cannot be applied after each other. So for example, we could have rules . . . → f(. . . , x)J x > 0 K and f (. . . , y) → J y < 0 K. Then our unifier σ would at least rename x and y to a common variable v, and we would then find (x > 0)σ ∧ (y < 0)σ = v > 0 ∧ v < 0 to be unsatisfiable. In such cases, the two rules cannot be evaluated after each other and we do not need to add an edge. In all other cases, we add an edge between the two rules to our rule graph.

1This is called a “dependency graph” in [GTSF06] and a “termination graph” in [FKS11]. As we do

not use the dependency pairs forming the basis of [GTSF06], and want to avoid confusion with the termination graphs from Chapter 2, we choose yet another new name.

Example 3.4 (Rule Graph) We consider the int-TRS R with the following three rules: f(x1, y1, a) → f(x01, y1, a) J x1+ y1 = 2 · z1∧ x 0 1 = x1+ 1K (3.1) f(x2, y2, a) → f(x02, y 0 2, b) J x2+ y2 = 2 · z2∧ x 0 2 = x2+ 1 ∧ y20 = y2+ 1K (3.2) f(x3, y3, b) → f(x3, y30, a) J y 0 3 = x 0 3+ 1K (3.3)

The rule graph for this system is surprisingly simple:

(3.1) (3.2) (3.3)

To construct the graph, we have to check each potential edge individually. For example, for the edge (3.1) to (3.2), we find the unifier σ = [x2/(x1+ 1), y2/y1]. We then proceed to check the following condition for satisfiability:

(x1+ y1 = 2 · z1∧ x01 = x1+ 1) ∧ ((x1+ 1) + y1 = 2 · z2∧ x02 = (x1+ 1) + 1 ∧ y20 = y1+ 1) However, x1 + y2 = 2 · z1 ∧ (x1+ 1) + y1 = 2 · z2 is unsatisfiable, so we do not need to draw an edge. For the edge from (3.2) to (3.1), we cannot find a unifier, as the constants b and a in the third argument of f clash. The remaining edges are handled similarly.

Based on RG, we present the first simple processor for int-TRSs, which we use to split a rewrite system into several smaller subsystems, which can then be analysed independently. For this, we use the SCCs2 of the rule graph. Obviously, any non-terminating reduction eventually only uses rules from a single SCC, and thus it suffices to show that no infinite reduction is possible for each SCC individually.

Theorem 3.5 (Rule Graph Processor) Let R be an int-TRS and S1, . . . , Snthe non-

trivial SCCs of RG(R). Then RGProc(R) = {S1, . . . , Sn} is a sound and complete pro-

cessor.

For the proof, we refer to [FKS11, Hoe12].

2A non-trivial SCC S of G is a set of nodes such that each of the nodes in S has a non-empty path to