Using Automatic Memoization As a Software Engineering Tool in Real-World AI Systems
Total Page:16
File Type:pdf, Size:1020Kb
Using Automatic Memoization as a Software Engineering Tool in Real-World AI Systems James Mayfield Tim Finin Marty Hall Computer Science Department Eisenhower Research Cent er , JHU / AP L University of Maryland Baltimore County Johns Hopkins Rd. Baltimore, MD 21228-5398 USA Laurel, MD 20723 USA Abstract The principle of memoization and examples of its Memo functions and memoization are well-known use in areas as varied as lo?; profammi2 v6, 6, 21, concepts in AI programming. They have been dis- functional programming 41 an natur anguage cussed since the Sixties and are ofien used as ezamples parsing [ll]have been described in the literature. In in introductory programming texts. However, the au- all of the case^ that we have reviewed, the use of tomation of memoization as a practical sofiware en- memoization was either built into in a special purpose gineering tool for AI systems has not received a de- computing engine (e.g., for rule-based deduction, or tailed treatment. This paper describes how automatic CFG parsing), or treated in a cursory way as an ex- memoization can be made viable on a large scale. It ample rather than taken seriously as a practical tech- points out advantages and uses of automatic memo- nique. The automation of function memoization as ization not previously described, identifies the com- a practical software engineering technique under hu- ponents of an automatic memoization facility, enu- man control has never received a detailed treatment. merates potential memoization failures, and presents In this paper, we report on our experience in develop- a publicly available memoization package (CLAMP) ing CLAMP, a practical automatic memoization pack- for the Lisp programming language. Experience in ap- age for Common Lisp, and its use in a large-scale AI plying these techniques in the development of a large project. planning system is briefly discussed. By means of illustration, consider the divided- difference algorithm dd, which is shown in pseudo- 1 Introduction code in Figure 1. This algorithm is used to deter- Memo functions and memoization are well known mine coefficients of interpolated polynomials. The al- concepts in AI programming. They have been dis- gorithm, which is a standard one in numerical meth- cussed since the Sixties and are often used as examples ods, is taken directly from Numerical Mathematics in introductory programming texts [15, 12, 13). The and Computing [l]. The application itself is not partic- term “memoization” is derived from the term ‘memo ularly important; it is the run-time behavior of the al- function,” which was coined by Donald Michie 91. It gorithm that is of interest to us here. The call tree for refers to the tabulation of the results of a set of c1 cula- the divided difference algorithm dd forms a network tions to avoid repeating those calculations. Automatic rather than a tree; thus, a recursive call to dd with par- memoization refers to a method by which an ordinary ticular arguments may be repeated many times during function can be changed mechanically into one that a single computation. For example, a call to dd with memoizes or caches its results. We place the decision low=l and high=lO will generate one recursive call about which functions to memoize in the hands of the with low=2 and high=lO and another recursive call user; this contrasts with the approach of Mostow and with low=l and hi h=9; each of these will in turn Cohen [lo], which tries to automatically infer which make a recursive calfwith low=2 and high=9. Mem- functions should be memoized. These two approaches oization of dd causea each calculation to be performed to automatic memoization are not incompatible, al- only once and stored in a table; thereafter, each re- though as Mostow and Cohen point out, the latter peated recursive call is replaced by a table lookup of approach is not a practical tool. the appropriate value. Memoization is particularly apt for AI applications. Figure 2 compares the performance of memoized Rapid prototyping is a hallmark of AI programming. and unmemoized versions of a Lisp’ implementation Automatic memoization allows the programmer to of the divided difference algorithm, using f(n) = write certain functions without regard to efficiency, T cos(n) and the first n natural numbers as arguments. while being assured of reasonable performance. By al- Since a single function call on n points generates two lowing the programmer to avoide these efficiency con- recursive calls on n - 1 points, the unmemoized ver- cerns at the outset, automatic memoization facilitates the kind of exploratory programming used in the de- ‘Throughout this paper, we use the name Lisp to refer to velopment of most AI systems. the language Common Lisp, as described in Steele [14]. 1043-0989/95$4.00 0 1995 IEEE ., pares the use of automatic memoization to the alterna- tive of rewriting code by hand. Section four describes ; divided-difference algorithm the general components that should be present in any dd(points: array, automatic memoization facility. Section five presents lov: array-index, problems inherent in the use of memoization and the high: array-index, various ways to address them. Section six describes fn: function) our experience in using automatic memoization in in begin the development of SMS [17], a decision support sys- if low - high then tem that provides submarine crews with situational return fn(pointsClov1) awareness and operational advice to reduce detectabil- else return (dd(points, lov+l, high, fn) - ity. dd(points, loo, high-1, fn)) / (pointschigh] - pointsClov1) 2 Uses of Memoization end There are four main uses of automatic memoiza- tion. Two of these involve the avoidance of redundant calculation, first, within a single function invocation, and second, across invocations. The third use of auto- Figure 1: The divided difference algorithm for deter- matic memoization is as a pre-calculation tool, while mining coefficients of interpolated polynomials can be the fourth is as a timing and profiling tool. These are elegantly written using recursion and made efficient not conjectured uses but ones which we found to be through the use of automatic memoization. effective in many situations in a large, real-world AI application. We discuss each of these uses in more detail in the following subsections. n I Unmemoized I Memoized I Memoized I 2.1 Repetition within a Function Call I (first run) I (subsequent runs) 1 The most common use of memoization is to avoid 0.0006 the repetition of sub-calculations within a single func- 0.0006 tion call. In the divided difference example presented 0.22 0.0006 above, there were many repeated recursive calls within 0.0007 a single function invocation. This type of repetition is common. For example, a simple recursive backtrack- 1 173 0.4 0.0007 ing parser may parse the same constituent many times 100 Centuries 25.0 0.002 during a single parse; thus its performance is poor. Norvig [ll]has shown that such an algorithm can ob- tain the performance of chart parsing [7 or of Earley's Figure 2: This table shows the benefits of memoization algorithm [3] through the application or memoization. on a Lisp implementation of dd. The time complexity is reduced from e(2")to O(n2)for initial runs and to 2.2 Repetition over Time near-constant time on subsequent ones. In a team programming environment different sec- tions of a system, written by different programmers, may access the same function. Alternatively, in an in- sion has time complexity. After memoization, teractive system the user may invoke calculations at e(2") different times that make use of some the same pieces. the first invocation requires O(n2) time, since no sub- In these cases, there is no central routine which could sequence of points is calculated more than once, and manage the calling sequence to avoid repetition of the there are (nz + n)/2 subsequences. Subsequent invo- calculations. The only alternative to automatic mem- cations take near-constant time. oization in such cases is to have the routine in question This algorithm for divided difference is typical of a manage its own data structures to cache previous re- large class of algorithms which have very elegant re- sults. cursive definitions which are simply unusable for most real problems. The conventional response to this sit- 2.3 Persistence uation is to manually rewrite the algorithm in a dy- The preceding subsections showed that memoiza- namic programming style. Of course, such manual tion can eliminate the repeated invocation of expen- rewriting of the code takes effort and involves the risk sive calculations. These two applications of memoiza- of introducing new bugs. An attractive alternative is tion are useful when it is feasible to perform the first to use an automatic memoization package to convert invocation of a function at run-time. Memoization is the elegant but inefficient recursive algorithm into a also useful when even the first invocation is too ex- useful one. This is attractive, of course, only if such pensive to perform at run-time. a package can address the practical problems faced in Use of functions that are too expensive to calcu- real application. late at run-time is usually done by building a special In the next section we describe the some of the as- purpose data file, and storing in it the results of an pects and uses of automatic memoization not previ- off-line execution of the expensive routine. Then, the ously described in the literature. Section three com- function in question is modified to access that file. 88 Automatic memoization provides a method to pre- presents a straightforward method for calculating di- calculate a function without the overhead of a hand- vided differences in the proper order to get the same crafted solution.