Document not found! Please try again

Programming with Transformation Rules

16 downloads 80175 Views 167KB Size Report
programming with transformation rules, and illustrate their usefulness with some ... We write expr →lbl if the application of rule lbl to expr fails, and expr →lbl if it.
Programming with Transformation Rules Mircea Marin1 and Temur Kutsia2 1

Johann Radon Institute for Computational and Applied Mathematics Austrian Academy of Sciences A-4040 Linz, Austria [email protected] 2 Research Institute for Symbolic Computation Johannes Kepler University A-4232 Hagenberg, Austria [email protected]

Abstract. Transformation rules are a convenient means to specify partially defined and nondeterministic computations. We describe a rulebased programming system which has primitive operators for defining elementary rules and for computing with unions, compositions, reflexivetransitive closures, and normal forms of transformation rules. To this end, we have implemented a Mathematica package called FunLog, which provides full support for programming with the primitive operators proposed by us. We give a brief account to the main concepts and constructs for programming with transformation rules, and illustrate their usefulness with some interesting examples.

1

Introduction

A transformation rule (rule for short) as an abstraction which enables a convenient and easily understandable way to specify partially defined and nondeterministic computations. More precisely, a rule has a specification of the form lbl :: patt :> rhs

(1)

where lbl is the rule name, patt is a pattern expression and rhs specifies the computation of a result from the bindings of the variables which occur in patt. The expression patt :> rhs is called the code of the rule lbl . The main usage of rules is to act with them them on various expressions. The attempt to apply a rule of type (1) on an expression expr proceeds as follows: 1. 2. 3. 4.

M := enumeration of all matchers between expr and patt. If M = ∅ then fail else goto 3. θ := first(M), M := rest(M ), V := enumeration of all values of θ(rhs). If V = ∅ then goto 3 else return first(V).

We write expr "→lbl if the application of rule lbl to expr fails, and expr →lbl if it succeeds. In order to support programming with rules, we have designed and implemented a rule-based system called FunLog. We decided to implement FunLog in the Mathematica language mainly because:

1. Mathematica has advanced features for pattern matching and for computing with transformation rules. These features provide a very good support for implementing a full fledged rule-based system, 2. Mathematica has powerful libraries which support symbolic computations, 3. rule-based programming, as envisioned by us, could be used efficiently to implement various reasoners which could be integrated in the Theorema framework [2] for proving, solving and computing. Since Theorema is implemented in Mathematica, a Mathematica implementation of a powerful rule-based system is intended to constitute a convenient programming tool for Theorema developers. There are two sources of non-determinism in FunLog: non-unique matches and non-unique ways to evaluate a partially defined computation. Clearly, the result of an application expr →lbl depends on the enumerations of matchers (M) and values (V) which are built into a particular implementation. These enumeration strategies are relevant to the programmer and are described in the specification of the operational semantics of our implementation. In the sequel we write expr 1 →lbl expr 2 if we can identify two enumerations, for M and V, which render the result expr 2 for the application expr 1 →lbl . Rules can be combined with various combinators into more complex rules which capture the most common ways of programming computations. FunLog provides implementations for the following combinators: choice: expr 1 →lbl 1 |...|lbl n expr 2 iff expr 1 →lbl i expr 2 for some i ∈ {1, . . . , n} composition: expr 1 →lbl 1 ◦lbl 2 expr 2 iff expr 1 →lbl 1 expr and expr →lbl 2 expr 2 for some expr reflexive-transitive closure: expr 1 →Repeat[lbl 1 ,lbl 2 ] expr 2 iff expr 1 →∗lbl 1 expr →lbl 2 expr 2 where →∗lbl 1 is the reflexive-transitive closure of →lbl 1 rewriting: If lbl is declared to be a rewrite rule corresponding to the rule lbl 1 then expr 1 →lbl expr 2 iff there exists a position p in expr 1 such that expr 1 |p →lbl 1 expr and expr 2 = expr 1 [expr ]p . Here, expr 1 |p is the subexpression of expr at position p, and expr 1 [expr ]p is the result of replacing the subexpression at position p by expr in expr 1 . normal form: expr 1 →NF[lbl] expr 2 iff expr 1 →∗lbl expr 2 and expr 2 "→lbl . The implementation of FunLog is compositional, i.e., the meaning and enumeration strategy of each program construct can be defined in terms of the meanings and enumeration strategies of the component rules. The rest of this paper is structured as follows. Section 2 explains the main features which underly our rule-based computation: nondeterminism and partially defined computations. The peculiar features of non-unique pattern matching and multiple ways of evaluating an expression are briefly explained and illustrated with examples. In Section 3 we introduce our main programming constructs and describe their intended meaning. Section 4 gives a brief account to some successful applications of FunLog and indicate applications which could be easily and efficiently implemented in FunLog. Section 5 concludes.

2

Nondeterminism and Partially Defined Computations

The main sources of nondeterminism in FunLog are multiple matches and multiple ways to evaluate a partially defined computation. 2.1

Multiple Matches

The pattern matching mechanism of FunLog is essentially the pattern matching mechanism of Mathematica [8, Sect. 2.3.8], which allows to define pattern constructs which have no unique matchers. Such patterns can be specified if we employ sequence variables1 or alternative patterns via the ”|” construct. Example 1. In Mathematica, the pattern {x the list {a, 2, 1, 2, b} in 3 possible ways:

, 1, y

} | {y

, 2, x

} matches

{x %→ !a, 2", y %→ !2, b"} {y %→ !a", x %→ !1, 2, b"} {y %→ !a, 2, 1", x %→ !b"}. The symbols x and y in the pattern are followed by 3 underscores, and therefore they denote variables which can be bound to an arbitrary sequence of elements. To improve the readability of matchers, we have written the bindings of sequence variables between ! and ". The first matcher is computed by matching with the first alternative pattern, whereas the last two can be computed by matching with the second alternative pattern. ' &

The Mathematica interpreter relies on the following built-in enumeration strategy [8, Sect. 2.3.8]: it tries first those matches that assign the shortest sequences of arguments to the first sequence variables that are encountered while traversing the pattern in a leftmost-innermost manner. Thus, the default enumeration of matches (M) is the enumeration strategy implemented in the Mathematica interpreter. 2.2

Multiple Values of Partially Defined Computations

In FunLog, partially defined computations are specified by attaching side conditions to patt or in rhs of (1). We write expr /; cond for an expression expr whose instances θ(expr ) are defined iff θ(cond ) evaluates to True. The expression cond is called the side condition which determines whether a certain instance of expr is defined or not. Thus, a rule lbl :: patt :> expr /; cond

(2)

defines a partial computation such that expr 1 →lbl expr 2 iff expr 2 = θ(expr 1 ), where θ is a matcher between expr 1 and patt such that θ(cond ) yields True. 1

They are called named sequence patterns in the Mathematica book [8].

Example 2. The pattern x − y/; (IntegerQ[x] ∧ IntegerQ[y] ∧ (x > y)) specifies a binary subtraction operation − : Z × Z → Z which is defined only for pairs (x, y) ∈ Z × Z with x > y.

It is often desirable to have a mechanism for sharing variables between cond and expr . In particular, we would like to instantiate some variables during the evaluation of cond and, if cond evaluates to True, to employ their values in the evaluation of expr . In FunLog, we achieve this by enclosing the evaluation of expr /; cond in a Block construct which declares the variables shared between expr and cond . For example, the following sequence of pseudo-code Block[{res}, res/; (expr →lbl res)]

(3)

has the variable res shared: if expr →lbl holds, the evaluation of the side condition binds res to one of the values v for which expr →lbl v and the evaluation of (3) resumes with v. The actual value of v returned by FunLog depends on the enumeration of values for res. In this case, the enumeration depends only on the enumeration of the matchers with the pattern of rule lbl . Example 3. The rule ”split” :: {x

,y

} :>{x}/; (Length[{x}] > Length[{y}])

takes as input a list L and computes a prefix sublist of L whose length is longer than half the length of L. The attempt to apply rule ”split” to {a, b, c} proceeds as follows: 1. It starts to enumerate the matchers between {a, b, c} and {x order which is specific to the Mathematica interpreter, i.e.:

,y

} in the

{x %→ !", y %→ !a, b, c"}, {x %→ !a", y %→ !b, c"}, {x %→ !a, b", y %→ !c"}, . . . 2. The enumeration is generated until we reach a matcher θ for which the condition θ(Length[{x}] > Length[{y}]) holds. In this example, the enumeration stops when it reaches the matcher θ =%→ !a, b", y %→ !c"} and the computation resumes with the value {a, b} of the instance θ({x}).

3

Programming Constructs

A rule with name lbl is applied to an expression expr 1 via the call ApplyRule[expr 1 , lbl ] which behaves as follows: – If expr 1 "→lbl then return expr 1

(4)

– If expr 1 →lbl then return the first expr 2 for which expr 1 →lbl expr 2 . The call ApplyRuleList[expr 1 , lbl ]

(5)

returns the list of values {expr 2 | expr 1 →lbl expr 2 }. FunLog provides a number of useful constructs to build up rules. These constructs are described in the remainder of this section. 3.1

Basic rule

A basic rule is a rule named by a string, whose code is given explicitly as a Mathematica transformation rule. A basic rule lbl :: patt :> rhs is declared by DeclareRule[patt :> rhs, lbl ]

(6)

We recall that expr 1 →lbl expr 2 iff there is a matcher θ between expr 1 and patt for which θ(rhs) evaluates to expr 2 . Enumeration strategy. The enumeration strategy of basic rules depends only on the enumeration strategy of matches. Example 4. The rule ”perm” introduced by the declaration DeclareRule[{x

m ,y

,n ,z

}/; (m > n) :>{x, n, y, m, z}, ”perm”]

takes as input a list of elements and permutes the elements which occur in descending order. The outcome of the call ApplyRule[{1, 2, 5, 4, 3}, ”perm”] {1, 2, 4, 5, 3} yields the instance {1, 2, 4, 5, 3} = θ({x, n, y, m, z}) corresponding to the matcher θ = {x %→ !1, 2", m %→ 5, y %→ !", n %→ 4, z %→ !3"}. Note that this is the first matcher found by the enumeration strategy of the Mathematica interpreter, for which θ(m > n) holds. ' & 3.2

Choice

lbl 1 | . . . | lbl n denotes a rule whose applicative behavior is given by expr 1 →lbl 1 |...|lbl n expr 2 iff expr 1 →lbl i expr 2 for some i ∈ {1, . . . , n}.

(7)

Enumeration strategy. The enumeration of the steps expr 1 →lbl 1 |...|lbl n starts with the enumeration of the steps expr 1 →lbl 1 , followed by the enumeration of the steps expr 1 →lbl 2 , and so on up to the enumeration of the steps expr 1 →lbl n . Example 5. Consider the declarations DeclareRule[{x m , y , n , z } :> False/; (m > n), ”test”]; DeclareRule[ List :> True, ”else”]; Here, List is a pattern variable which matches any list structure. Then ApplyRule[L, ”test” | ”else”] yields True iff L is a list with elements in ascending order. This behavior is witnessed by the calls: ApplyRule[{1, 2, 4, 5, 3}, ”test” | ”else”] False ApplyRule[{1, 2, 3, 4, 5}, ”test” | ”else”] True The first call yields False because {1, 2, 4, 5, 3} →”test” False with the matcher θ = {x %→ !1, 2", m %→ 5, y %→ !", n %→ 4, z %→ !3"}. The second call yields True ' & because {1, 2, 3, 4, 5} "→”test” and {1, 2, 3, 4, 5} →”else” True. 3.3

Composition

lbl 1 ◦ lbl 2 denotes a rule whose applicative behavior is given by expr 1 →lbl 1 ◦lbl 2 expr 2 iff expr1 →lbl 1 expr →lbl 2 expr 2 for some expr .

(8)

Enumeration strategy. The enumeration of values expr 2 for which the relation expr 1 →lbl 1 ◦lbl 2 expr 2 holds, proceeds by enumerating all steps expr →lbl 2 expr 2 during an enumeration of the steps expr 1 →lbl 1 expr . Example 6. The following piece of code DeclareRule[x Real/; x < 0 :> √ x + 7, ”f”] DeclareRule[x Real/; x > 0 :> x, ”g”] defines rules ”f” and ”g” which encode the partially defined functions ! f : (−∞, 0.) → R, x %→ √ x + 7, g : (0., ∞) → R, x %→ x. Then ”g” ◦ ”f” denotes a rule which encodes the function g ◦ f : (−7, 0) → R.

' &

3.4

Reflexive-Transitive Closures

If lbl ∈ {Repeat[lbl 1 , lbl 2 ], Until[lbl 2 , lbl 1 ]} then

expr 1 →lbl expr 2 iff expr 1 →∗lbl 1 expr →lbl 2 expr 2 for some expr

(9)

where →∗lbl 1 denotes the reflexive-transitive closure of →lbl 1 . These two constructs differ only with respect to the enumeration strategy of the possible reduction steps. (See below.) Enumeration strategies. The enumeration of expr 1 →Repeat[lbl 1 ,lbl 2 ] expr 2 proceeds by unfolding the recursive definition: Repeat[lbl 1 , lbl 2 ] = lbl 1 ◦ Repeat[lbl 1 , lbl 2 ] | lbl 2 ,

whereas the enumeration of expr 1 →Until[lbl 1 ,lbl 2 ] expr 2 proceeds by unfolding the recursive definition: Until[lbl 2 , lbl 1 ] = lbl 2 | lbl 1 ◦ Until[lbl 2 , lbl 1 ].

Example 7 (Sorting). Consider the declarations of basic rules DeclareRule[x :> x, ”Id”]; DeclareRule[{x , m , y , n , z

}/; (m > n) :>{x, n, y, m, z}, ”perm”];

Then the enumeration strategy of Repeat[”perm”, ”Id”] ensures that the application of rule Repeat[”perm”, ”Id”] to any list of integers yields the sorted version of that list. For example ApplyRule[{3, 1, 2}, Repeat[”perm”, ”Id”]] yields {1, 2, 3} via the following sequence of transformation steps

{3, 1, 2} →”perm” {1, 3, 2} →”perm” {1, 2, 3} →”Id” {1, 2, 3}. ' &

3.5

Rewrite Rules

FunLog provides the following mechanism to define a rule which performs rewriting with respect to a given rule: RWRule[lbl 1 , lbl , Traversal → . . . , Prohibit → . . .]

(10)

This call declares a new rule named lbl such that expr 1 →lbl expr 2 iff there exists a position p in expr 1 such that expr 1 |p →lbl 1 expr and expr 2 = expr 1 [expr ]p . Here, expr 1 |p is the subexpression of expr at position p, and expr 1 [expr ]p is the result of replacing the subexpression at position p by expr in expr 1 . The option Traversal defines the enumeration ordering of rewrite steps (see below), whereas the option Prohibit restricts the set of positions allowed for rewriting. If the option Prohibit → {f1 , . . . , fn } is given, then rewriting is prohibited at positions below occurrences of symbols f ∈ {f1 , . . . , fn }. By default, the value of Prohibit is {}, i.e., rewriting can be performed everywhere.

Enumeration strategies. The enumeration strategy of rewrite steps can be controlled via the option Traversal which has the default value ”LeftmostIn”. If the option Traversal → ”LeftmostOut” is given, then the rewriting steps are enumerated by traversing the rewriting positions in leftmost-outermost order. If Traversal → ”LeftmostIn” is given, then the rewriting steps are enumerated by traversing the rewriting positions in leftmost-innermost order. Example 8 (Pure λ-calculus). In λ-calculus, a value is an expression which has no β-redexes outside λ-abstractions. We adopt the following syntax for λ-terms: term ::= | x | app[term1 , term2 ] | λ[x, term]

terms : variable application abstraction

β-redexes are eliminated by applications of the β-conversion rule, which can be encoded in FunLog as follows: DeclareRule[app[λ[x , t1 ], t2 ] :> repl[t1 , {x, t2}], ”β”]

where repl[t1 , {x, t2}] replaces all free occurrences of x in t1 by t2. A straightforward implementation of repl in Mathematica is shown below2 : repl[λ[x , t ], {x , }] := λ[x, t]; repl[x , {x , t }] := t; repl[λ[x , t ], σ ] := λ[x, repl[t, σ]]; repl[app[t1 , t2 ], σ ] := app[repl[t1, σ], repl[t2, σ]]; repl[t , ] := t; The computation of a value of a λ-term proceeds by repeated reductions of the redexes which are not inside abstractions. In FunLog, the reduction of such a redex coincides with an application of the rewrite rule ”β-elim” defined by RWRule[”β”, ”β-elim”, Prohibit → {λ}]

The following calls illustrate the behavior of this rule:

t := app[z, app[λ[x, app[x, λ[y, app[x, y]]]], λ[z, z]]]; t1 := ApplyRule[t, ”β-elim”] app[z, app[λ[z, z], λ[y, app[λ[z, z], y]]]] t2 := ApplyRule[t1, ”β-elim”] app[z, λ[y, app[λ[z, z], y]]] t3 := ApplyRule[t2, ”β-elim”] app[z, λ[y, app[λ[z, z], y]]] Thus, t2 is a value of t. To compute the value of t directly, we could call ApplyRule[t, Repeat[”β-elim”, ”Id”]] app[z, λ[y, app[λ[z, z], y]]] 2

' &

The Mathematica definitions are tried top-down, and this guarantees a proper interpretation of the replacement operation.

3.6

Normal Form

NF[lbl ] denotes a rule whose applicative behavior is given by expr →NF[lbl] expr 2 iff expr 1 →∗lbl expr 2 and expr 2 "→lbl .

(11)

Enumeration strategy. The enumeration strategy of NF[lbl ] is obtained by unfolding the recursive definition NF[lbl ] = NFQ[lbl] | lbl ◦ NF[lbl ]

(12)

where NFQ[lbl] :: x :> x/; (x "→lbl ). 3.7

Abstraction

The abstraction principle is provided in FunLog via the construct SetAlias[expr , lbl ]

(13)

where lbl is a fresh rule name (a string identifier) and expr is an expression built from names of rules already declared with the operators |, ◦, Repeat and Until described before. For example, the call SetAlias[Repeat[”perm”, ”Id”], ”sort”] declares a rule named ”sort” whose applicative behavior coincides with that of the construct Repeat[”perm”, ”Id”].

4

Applications

Obviously, the range of applications which can be tackled with FunLog is very large. Recently, we employed FunLog to implement various unification procedures in theories with sequence variables, and integrated them in the equational prover of Theorema [4]. These procedures can be described as finite sets of inference rules [3] which have efficient and straightforward implementations in FunLog [7]. When rule applications are viewed as deduction steps then FunLog becomes a deductive system. In this context, FunLog can be employed to generate proof trees which correspond to various deduction strategies. The methods ApplyRule and ApplyRuleList recognize the option TraceStyle which controls the output produced by the call. The default value of TraceStyle is ”None”, and in this case the call yields the value (resp. list of values) corresponding to the rule application. If TraceStyle is "Object" then the methods yield an abstract proof object which contains all information needed to trace the deduction steps which lead to the computation of the corresponding value(s). If TraceStyle is "Notebook" then the methods yield a Mathematica notebook which contains a human-readable presentation of the proof object.

5

Conclusions and Future Work

The primitive operators implemented in FunLog reflect the most common ways to build up complex computations from simpler ones. But we also expect that our future attempts to solve new problems will reveal new programming constructs which are desirable for making our programming style more expressive. Currently, we investigate how these programming constructs can be employed to explore the search space for proofs in the Theorema system [1, 2]. We also believe that our programming constructs could be used efficiently by the Theorema users and developers to write reasoners. Another direction of future work is to introduce control mechanisms for pattern matching with sequence variables. The current implementation of FunLog relies entirely on the enumeration strategy of matchers which is built into the Mathematica interpreter. However, there are many situations when this enumeration strategy is not desirable. We have already addressed this problem in [5, 6] and implemented the package Sequentica with language extensions which can overwrite the default enumeration strategy of the Mathematica interpreter. The integration of those language extensions in FunLog will certainly increase the expressive power of our rule-based system. We are currently working on integrating Sequentica with FunLog. The current implementation of FunLog can be downloaded from http://www.score.is.tsukuba.ac.jp/~mmarin/FunLog/ Acknowledgements. Mircea Marin has been supported by the Austrian Academy of Sciences. Temur Kutsia has been supported by the Austrian Science Foundation (FWF) under Project SFB F1302.

References 1. Bruno Buchberger, Claudio Dupr´e, Tudor Jebelean, Franz Kriftner, Koji Nakagawa, Daniela V˘ asaru, and Wolfgang Windsteiger. The Theorema project: A progress report. In M. Kerber and M. Kohlhase, editors, Symbolic Computation and Automated Reasoning. Proceedings of Calculemus’2000 Conference, pages 98–113, St. Andrews, UK, August 6–7 2000. 2. Bruno Buchberger, Tudor Jebelean, Franz Kriftner, Mircea Marin, Elena Tomut¸a, and Daniela V˘ asaru. A survey of the Theorema project. In W. K¨ uchlin, editor, Proceedings of the International Symposium on Symbolic and Algebraic Computation, ISSAC’97, pages 384–391, Maui, Hawaii, US, July 21–23 1997. ACM Press. 3. Temur Kutsia. Unification with Sequence Variables and Flexible Arity Symbols and its Extension with Pattern-Terms. In J. Calmet, B. Benhamou, O. Caprotti, L. Henocque, and V. Sorge, editors, Artificial Intelligence, Automated Reasoning and Symbolic Computation. Proceedings of Joint AISC’2002 and Calculemus’2002 Conferences, volume 2385 of LNAI, pages 290–304, Marseille, France, July 1–5 2002. Springer Verlag.

4. Temur Kutsia. Equational prover of Theorema. In R. Nieuwenhuis, editor, Proceedings of the 14th International Conference on Rewriting Techniques and Applications (RTA’03), volume 2706 of LNCS, pages 367–379, Valencia, Spain, 9–11 June 2003. Springer Verlag. 5. Mircea Marin. Functional Programming with Sequence Variables: The Sequentica Package. In J. Levy, M. Kohlhase, J. Niehren, and M. Villaret, editors, Proceedings of the 17th International Workshop on Unification (UNIF 2003), pages 65–78, Valencia, June 2003. 6. Mircea Marin and Dorin T ¸ epeneu. Programming with Sequence Variables: The Sequentica Package. In Peter Mitic, Philip Ramsden, and Janet Carne, editors, Challenging the Boundaries of Symbolic Computation. Proceedings of 5th International Mathematica Symposium (IMS 2003), pages 17–24, Imperial College, London, July 7–11 2003. Imperial College Press. 7. Mircea Marin and Temur Kutsia. On the Implementation of a Rule-Based Programming System and some of its Applications. In Boris Konev and Renate Schmidt, editors, Proceedings of the Fourth International Workshop on the Implementation of Logics, Almaty, Kazakhstan, September 26 2003. To appear. 8. Stephen Wolfram. The Mathematica Book. Wolfram Media, Champaign, Illinois, fourth edition, 1999.

Suggest Documents