<<

Dynamic Programming via Static Incrementalization

 

Yanhong A. Liu and Scott D. Stoller

Abstract

Dynamic programming is an imp ortant design technique. It is used for solving

problems whose solutions involve recursively solving subproblems that share subsubproblems.

While a straightforward recursive program solves common subsubproblems rep eatedly and of-

ten takes exp onential time, a dynamic programming algorithm solves every subsubproblem just

once, saves the result, reuses it when the subsubproblem is encountered again, and takes p oly-

nomial time. This pap er describ es a systematic metho d for transforming programs written as

straightforward into programs that use dynamic programming. The metho d extends

the original program to all p ossibly computed values, incrementalizes the extended pro-

gram with resp ect to an input increment to use and maintain all cached results, prunes out

cached results that are not used in the incremental computation, and uses the resulting in-

cremental program to form an optimized new program. Incrementalization statically exploits

semantics of b oth control structures and data structures and maintains as invariants equalities

characterizing cached results. The principle underlying incrementalization is general for achiev-

ing drastic program sp eedups. Compared with previous metho ds that p erform or

tabulation, the metho d based on incrementalization is more powerful and systematic. It has

b een implemented and applied to numerous problems and succeeded on all of them.

1 Intro duction

Dynamic programming is an imp ortant technique for designing ecient [2, 44 , 13 ]. It

is used for problems whose solutions involve recursively solving subproblems that overlap. While a

straightforward recursive program solves common subproblems rep eatedly, a dynamic programming

algorithm solves every subproblem just once, saves the result in a table, and reuses the result when

the subproblem is encountered again. This can reduce the from exp onential to

p olynomial. The technique is generally applicable to all problems whose ecient solutions involve

memoizing results of subproblems [4, 5].

Given a straightforward , there are two traditional ways to achieve the e ect of dynamic

programming [13 ]: memoization [32 ] and tabulation [5 ].

Memoization uses a mechanism that is separate from the original program to save the result of

each function call or reduction [32 , 18 , 21 , 33 , 23 , 41 , 43 , 37 , 17 , 1 ]. The idea is to keep a separate

table of solutions to subproblems, mo dify recursive calls to rst lo ok up in the table, and then, if

the subproblem has b een computed, use the saved result, otherwise, compute it and save the result

in the table. This metho d has two advantages. First, the original recursive program needs virtually

no change. The underlying interpretation mechanism takes care of the table lling and lo okup.

Second, only values needed by the original program are actually computed, which is optimal in



The authors gratefully acknowledge the supp ort of NSF under Grant CCR-9711253. Authors' address: Computer

Science Department, Indiana University, Lindley Hall 215, Blo omington, IN 47405. Phone: 812-855-f4373,7979g.

Email: fliu,[email protected]. 1

a sense. Memoization has two disadvantages. First, the mechanism for table lling and lo okup

has an interpretiveoverhead. Second, no general strategy for table management is ecient for all

problems.

Tabulation determines what shap e of table is needed to store the values of all p ossibly needed

sub computations, intro duces appropriate data structures for the table, and computes the table

entries in a b ottom-up fashion so that the solution to a sup erproblem is computed using available

solutions to subproblems [5 , 12 , 38 , 37 , 9, 11 , 39 , 40 , 20 , 10 ]. This overcomes b oth disadvantages of

memoization. First, table lling and lo okup are compiled into the resulting program so no separate

mechanism is needed for the execution. Second, strategies for table lling and lo okup can be

sp ecialized to be ecient for particular problems. However, tabulation has two drawbacks. First,

it generally requires a thorough understanding of the problem and a complete manual rewrite of the

program [13 ]. Second, to statically ensure that all values p ossibly needed are computed and stored,

a table that is larger than necessary is often used; it may also include solutions to subproblems not

actually needed in the original computation.

This pap er presents a p owerful metho d that statically analyzes and transforms straightforward

recursive programs to eciently cache and use the results of needed subproblems at appropriate

program p oints in appropriate data structures. The metho d has three steps: (1) extend the original

program to cache all p ossibly computed values, (2) incrementalize the extended program, with

resp ect to an input increment, to use and maintain all cached results, (3) prune out cached results

that are not used in the incremental computation, and nally use the resulting incremental program

to form an optimized program. The metho d overcomes b oth drawbacks of tabulation. First, it

consists of static program analyses and transformations that are general and systematic. Second,

it stores only values that are necessary for the optimization; it also shows exactly when and where

subproblems not in the original computation are necessarily included.

Our metho d is based on static analyses and transformations studied previously by others [49 ,

8, 46 , 6 , 34 , 19 , 47 , 39 ] and ourselves [31 , 30 , 29 , 25 , 30 ] and improves them. Yet, all three steps are

simple, automatable, and ecient and have been implemented in a prototyp e system, CACHET.

The system has b een used to optimize many programs written as straightforward recursions, includ-

ing all dynamic programming problems found in [2 , 44 , 13]. Performance measurements con rm

drastic asymptotic sp eedups.

2 Formulating the problem

Straightforward solutions to many combinatorics and optimization problems can be written as

simple recursions [44 , 13 ]. For example, the matrix-chain-multiplication problem [13 , pages 302-

314] computes the minimum number of scalar multiplications needed by any parenthesization in

multiplying a chain of n matrices, where matrix i has dimensions p  p . This can b e computed

i1 i

as m(1;n), where m(i; j ) computes the minimum numb er of scalar multiplications for multiplying

matrices i through j and can b e de ned as: for i  j ,



0 if i = j

m(i; j )=

min fm(i; k )+m(k +1;j)+p  p  p g otherwise

i1 j

ik j 1 k

The longest-common-subsequence problem [13 , pages 314{320] computes the length c(n; m) of the

longest common subsequence of two sequences hx ;x ; :::; x i and hy ;y ; :::; y i, where c(i; j ) can

1 2 n 1 2 m

b e de ned as: for i; j  0,

(

0 if i =0 or j =0

c(i 1;j 1)+1 if i 6= 0 and j 6= 0 and x = y

c(i; j )=

i j

max(c(i; j 1);c(i 1;j)) otherwise 2

Both of these examples are literally copied from the textb o ok by Cormen, Leiserson, and Rivest [13 ].

These recursive functions can b e written straightforwardly in the following rst-order, call-by-

value functional . A program is a function f de ned by a set of mutually

0

recursive functions of the form

f (v ; :::; v ) , e

1 n

where an expression e is given by the grammar

e ::= v variable

j c(e ; :::; e ) constructor application

1 n

j p(e ; :::; e ) primitive function application

1 n

j f (e ;:::;e ) function application

1 n

j if e then e else e conditional expression

1 2 3

j let v = e in e binding expression

1 2

We include arrays as variables and use them for indexed access such as x and p ab ove. For

i j

convenience, we allow global variables to be implicit parameters to functions; such variables can

be identi ed easily for our language even if they are given as explicit parameters. Figure 1 gives

programs for the examples ab ove. Invariants ab out an input are not part of a program but are

written explicitly to b e used by the transformations. These examples do not use data constructors,

but our previous pap ers contain a numb er of examples that use them [31 , 30 , 29 ] and our metho d

handles them.

m(i; j ) where i  j

, if i = j then 0

c(i; j ) where i; j  0

else msub(i; j; i)

, if i =0 _ j =0 then 0

msub(i; j; k ) where i  k  j 1

else if x[i]=y [j ] then c(i 1;j 1) + 1

, let s = m(i; k )+m(k +1;j)+p[i 1]  p[k ]  p[j ] in

else max(c(i; j 1);c(i 1;j))

if k +1 = j then s

else min(s; msub(i; j; k + 1))

Figure 1: Example programs.

These straightforward programs rep eatedly solve common subproblems and take exp onential

time. We transform them into dynamic programming algorithms that p erform ecient caching and

take p olynomial time.

We use an asymptotic cost mo del for measuring time complexity. Assuming that all primitive

functions take constant time, we need to consider only values of function applications as candidates

for caching. Caching takes extra space, which re ects the well-known trade-o between time and

space. Our primary goal is to improve the asymptotic running time of the program. Our secondary

goal is to save space bycaching only values useful for achieving the primary goal.

Caching requires appropriate data structures. In Step 1, wecache all p ossibly computed results

in a recursive following the structure of recursive calls. Each no de of the tree is a tuple that

bundles recursive subtrees with the return value of the current call. We use <> to denote a tuple,

and we use selectors 1st,2nd,3rd, etc. to select the rst, second, third, etc. elements of a tuple.

In Step 2, cached values are used and maintained in eciently function calls on

slightly incremented inputs. We use an in x op eration  to denote an input increment op eration,

also called an input change (or up date) op eration. It combines a previous input x = hx ; :::; x i

1 n

0 0 0

and an increment parameter y = hy ; :::; y i to form an incremented input x = hx ; :::; x i = x  y ,

1 m

n

1

0

where each x is some function of x 's and y 's. An input increment op eration we use for program

j

k

i

optimization always has a corresp onding decrement op eration pr ev such that for all x, y , and 3

0 0 0

x , if x = x  y then x = pr ev (x ). Note that y need not be used. For example, an input

0 0 0 0

increment to function m in Figure 1 could be hx ;x i = hx ;x +1i or hx ;x i = hx 1;x i,

1 2 1 2

1 2 1 2

0 0 0 0

and the corresp onding decrement op erations are hx ;x i = hx ;x 1i and hx ;x i = hx +1;x i,

1 2 1 2

1 2 1 2

0

resp ectively. An input increment to a function that takes a list could b e x = cons(y; x), and the

0

corresp onding decrement op eration is x = cdr (x ).

In Step 3, cached values that are not used for an incremental computation are pruned away,

yielding functions that cache, use, and maintain only useful values.



For a function f in an original program, f denotes the function that caches all p ossibly computed

^

values of f ,andf denotes the pruned function that caches only useful values. We use x to denote

 ^

an un-incremented input and use r , r, and r^ to denote the return values of f (x), f (x), and f (x),

0 0

resp ectively. For any function g ,we use g to denote the incremental function that computes g (x ),

0 0 0

where x = x  y , using cached results ab out x suchas g (x). So, g maytake parameter x ,aswell

as extra parameters each corresp onding to a cached result. Figure 2 summarizes the notation.

Function Return Value Denoted as Incremental Function

0

f original value r f

0

 

f all p ossibly computed values r f

0

^ ^

f f useful values r^

Figure 2: Notation.

3 Step 1: Caching all p ossibly computed values

Consider a function f de ned by a set of recursive functions. Program f may use global variables,

0 0

such as x and y in function c(i; j ). A possibly computed value is the value of a function call that

is computed for some but not necessarily all valuesofthe global variables. For example, function

c(i; j ) computes the value of c(i 1;j 1) only when x[i] = y [j ] but will be extended to always

compute it. Such values o ccur exactly in branches of conditional expressions whose conditions

dep end on any global variable.



We construct a program f that caches all p ossibly computed values in f . We rst apply a

0 0

simple hoisting transformation to lift function calls out of conditional expressions whose conditions

dep end on global variables. We then apply an extension transformation to cache all intermediate

results, i.e., values of all function calls, in the return value.

Hoisting transformation. Hoisting transformation H st identi es conditional expressions whose

condition dep ends on any global variable and then applies the transformation

Hst[[if e then e else e ]]= let v = e in

1 2 3 2 2

let v = e in

3 3

if e then v else v

1 2 3

For example, the hoisting transformation leaves m and msub unchanged and transforms c into

c(i; j ) , if i =0 _ j =0 then 0

else let u = c(i 1;j 1) + 1 in

1

let u =max(c(i; j 1);c(i 1;j)) in

2

if x[i]=y [j ] then u else u

1 2

H st simply lifts up the entire sub expressions in the two branches, not just the function calls

in them. Administrative simpli cation p erformed at the end of the extension transformation will 4

identify computations that o ccur at most once in subsequent computations and unwind the corre-

sp onding bindings; thus computations other than function calls will b e put down into the appropri-

ate branches then. H st is simple and ecient. The resulting program has essentially the same size

as the original program, so H st do es not increase the running time of the extension transformation

or the running times of the later incrementalization and pruning.

If we apply the hoisting transformation on arbitrary conditional expressions, the resulting pro-

gram may run slower, b ecome non-terminating, or have errors intro duced. For conditional expres-

sions whose conditions dep end on global variables, we assume that b oth branches may b e executed

to terminate correctly regardless of the condition, which holds for the large class of combinatorics

and optimization problems we handle. By limiting the hoisting transformation on these conditional

expressions, we eliminated the last two problems. The rst problem is discussed in Section 6.

Extension transformation. For each hoisted function de nition f (v ; :::; v ) , e,we construct

1 n

a function de nition



f (v ; :::; v ) , Ext[[e]] (1)

1 n

where Ext[[e]], de ned in [30 ], extends an expression e to return a nested tuple that contains the

values of all function calls made in computing e, i.e., it examines sub expressions of e in applicative

order, intro duces bindings that name the results of function calls, builds up tuples of these values

together with the values of the original sub expressions, and passes these values from sub computa-

tions to enclosing computations. The rst comp onentof a tuple corresp onds to an original return

value. Next, administrative simpli cations clean up the resulting program. This yields a program



f that embeds values of all p ossibly computed function calls in its return value. For the hoisted

0

programs m and c, the extension transformation pro duces the following functions:

m(i; j ) , if i = j then < 0 >

msub(i; j; i) else

msub(i; j; k ) , let v = m(i; k ) in

1

m(k +1;j) in let v =

2

let s =1st(v )+1st(v )+p[i 1]  p[k ]  p[j ] in

1 2

if k +1 = j then

1 2

else let v = msub(i; j; k +1) in

< min(s; 1st(v ));v ;v ;v >

1 2

c(i; j ) , if i =0 _ j =0 then < 0 >

else let v =c (i 1;j 1) in

1

let v =c (i; j 1) in

2

let v =c (i 1;j) in

3

if x[i]=y [j ] then < 1st(v )+1;v ;v ;v >

1 1 2 3

else < max(1st(v ); 1st(v ));v ;v ;v >

2 3 1 2 3

4 Step 2: Static incrementalization

The essence of our metho d is to use and maintain cached values eciently as a computation



pro ceeds, i.e., we incrementalize f with resp ect to an input increment op eration . Precisely,we

0

 

transform f (x  y ) to use the cached value of f (x) rather than compute from scratch.

0 0

An input increment operation  corresp onds to a minimal up date to the input parameters. We

rst describ e a general metho d for identifying . We then give a powerful metho d, called static

0

 

incrementalization, that constructs an incremental version f for each function f in the extended

program and allows an incremental function to have multiple parameters that represent cached

values. 5

Input increment op eration. An input increment should re ect how a computation pro ceeds. In

general, a function mayhavemultiple ways of pro ceeding dep ending on the particular computations

involved. There is no general metho d for identifying all of them or the most appropriate ones. Here

we prop ose a metho d that can systematically identify a general class of them. The idea is to use a

minimal input change that is in the opposite direction of change compared to arguments of recursive

calls. Using the opp osite direction of change yields an increment; using a minimal change allows

maximum reuse, i.e., maximum incrementality.

Consider a recursively de ned function f . Formulas for the p ossible arguments of recursive calls

0

to f in computing f (x)can b e determined statically. For example, for function c(i; j ), recursive

0 0

calls to c have the set of p ossible arguments S = fhi 1;j 1i; hi; j 1i; hi 1;jig, and for function

c

m(i; j ), recursive calls to m have the set of p ossible arguments S = fhi; k i; hk +1;jiji  k  j 1g.

m

The latter is simpli ed from S = fha; ci; hc +1;bija  c  b 1;a = i; b = j g where a; b; c are fresh

m

variables that corresp ond to i; j; k in msub; the equalities are based on arguments of the recursive

calls involved (in this case msub); and the inequalities are obtained from the inequalities on these

arguments. The simpli cation here, as well as the manipulations b elow, can b e done automatically

using Omega [42 ].

Represent the arguments of recursive calls so that the di erences between them and x are

explicit. For function c, S is already in this form, and for function m, S is rewritten as fhi; j

c m

l i; hi + l; j ij1  l  j ig. Then, extract minimal di erences that cover all of these recursive

calls. The partial ordering on di erences is: a di erence involving fewer parameters is smaller; a

di erence in one parameter with smaller magnitude is smaller; other di erences are incomparable.

A set of di erences covers a recursive call if the argument to the call can b e obtained by rep eated

application of the given di erences. So, we rst compute the set of minimal di erences and then

remove from it each element that is covered bythe remaining elements. For function c, we obtain

fhi; j 1i; hi 1;jig, and for function m, we obtain fhi; j 1i; hi +1;jig. Elements of this set

represent decrement op erations. Finally, take the opp osite of each decrement op eration to obtain

an increment op eration ,intro ducing a parameter y if needed (e.g., for increments that use data

constructions). For function c, we obtain hi; j +1i and hi +1;ji, and for function m, we obtain

hi; j +1i and hi 1;ji. Even though nding an input increment op eration is theoretically hard in

general, it is usually straightforward.

Typically, a function involves rep eatedly solving common subproblems when it contains multiple

recursive calls to itself. If there are multiple input increment op erations, then any one may be

used to incrementalize the program and nally form an optimized program; the rest may b e used

to further incrementalize the resulting optimized program, if it still involves rep eatedly solving

common subproblems. For example, for program c, either hi; j +1i or hi +1;ji will lead to a nal

optimized program, and for program m,bothhi 1;ji and hi; j +1i need to b e used, and they may

b e used in either order.



Static incrementalization. Given a program f and an input increment op eration , incremen-

0

0 0



talization symb olically transforms f (x )forx = x  y to replace sub computations with retrievals

0



of their values from the value r of f (x). This exploits equality reasoning, based on control and

0

0



data structures of the program and prop erties of primitive op erations. The resulting program f

0



usesr  or parts ofr  as additional arguments, called cache arguments, and satis es: if f (x)=r and

0

0 0 0 1 0 0

 

(x ; r)=r . f (x )=r , then f

0 0

The idea is to establish the strongest invariants, esp ecially those ab out cache arguments, at

all calls and maximize their usage. At the end, unused candidate cache arguments are eliminated.

0 0 0 0 1

   

(x; y ; r)=r . slightly di erently: if f (x)=r and f (x  y )=r ,thenf In previous pap ers, we de ned f

0 0 0 0 6

Reducing running time corresp onds to maximizing uses of invariants; reducing space corresp onds

to maintaining weakest invariants for all uses. It is imp ortant that the metho ds for establishing and

using invariants are sp ecialized so that they are automatable. The precise algorithm is describ ed

b elow. Its use is illustrated afterwards using the running examples.

0 0

 

The algorithm starts with transforming f (x ) for x = x  y and f (x)=r and rst uses the

0 0

decrement op eration to establish an invariant ab out function arguments. More precisely, it starts

0 0

 

with transforming f (x ) with invariant f (pr ev (x )) =r , wherer  is a candidate cache argument. It

0 0

0

may use other invariants ab out x if given. Invariants given or formed from the enclosing conditions

and bindings are called context. The algorithm transforms function applications recursively. There

0 0

are four cases at a function application f (e ; :::; e ).

n

1

0 0

(1) If f (e ; :::; e ) sp ecializes, by de nition of f , under its context to a base case, i.e., an expression

1 n

with no recursive calls, then replace it with the sp ecialized expression.

0 0

(2) Otherwise, if f (e ; :::; e ) equals a retrieval from a cache argument based on an invariant

1 n

ab out the cache argument in its context, then replace it with the retrieval.

0 0 0

(3) Otherwise, if an incremental version f of f has been intro duced, then replace f (e ; :::; e )

n

1

0

with a call to f if the corresp onding invariants can b e maintained; if some invariants can not

0

b e maintained, then eliminate them and retransform from where f was intro duced.

0 0 0 0

(4) Otherwise, intro duce an incremental version f of f and replace f (e ; :::; e )withacalltof ,

n

1

as describ ed b elow.

In general, the replacement in case (1) is also done, rep eatedly, if the sp ecialized expression con-

tains only recursive calls whose arguments are closer to, and will equal after a b ounded number of

such replacements, arguments for base cases or arguments on which retrievals can be done. Since

a b ounded number of invariants are used at a function application, as describ ed b elow, the re-

transformation in case (3) can only b e done a b ounded numb er of times. So, the algorithm always

terminates.

0 0 0

), let Inv be the set of invariants To intro duce an incremental version f of f at f (e ; :::; e

n

1

0 0

ab out cache arguments or context information at f (e ; :::; e ). Those ab out cache arguments are

n

1

of the form g (e ; :::; e ) = e , where e is either a candidate cache argument in the enclosing

i i1 in ir ir

i

environment or a selector applied to such an argument. Those ab out context information are of

the form e = tr ue, e = false, or v = e, obtained from conditions or bindings. For simplicity,we

0

assume that all bound variables are renamed so that they are distinct. Intro duce f to compute

00 00 00 0 00 0 00 00

f (x ; :::; x ) for x = e ; :::; x = e , where x ; :::; x are fresh variables, and deduce invariants

1 n 1 1 n n 1 n

00 00 0 00 0 00

ab out x ; :::; x based on Inv. The deduction uses equations e = x ; :::; e = x to eliminate

n n n

1 1 1

variables in Inv and can be done automatically using Omega [42 ]. Resulting equations relating

00 00

; :::; x are used also to duplicate other invariants deduced. If a resulting invariant still uses x

n

1

00 00

a variable other than x ; :::; x , discard it. Finally, for each invariant ab out a cache argument,

n

1

replace its right hand side with a fresh variable, which b ecomes a candidate cache argument of

0 0

f . This yields the set of invariants now asso ciated with f . Note that invariants ab out cache

00 00 00 00 00 00

arguments have the form g (e ; :::; e ) = r , where e ; :::; e use only variables x ; :::; x , and

i i

n

1

i1 in i1 in

i i

r is a fresh variable. Among the left hand sides of these invariants, identify an application of f

i

00 00

whose arguments have a minimum di erence from x ; :::; x ; if such an application exists, denote

1 n

00 00

it f (e ; :::; e ).

1 n

0 00 00 00 00

To obtain a de nition of f , unfold f (x ; :::; x ) and then exploit conditionals in f (x ; :::; x )and

1 n 1 n

00 00 0

f (e ; :::; e ) (if it exists) and comp onents in the candidate cache arguments of f . To exploit condi-

n

1

00 00 00 00

tionals in f (x ; :::; x ), move function applications inside branches of the conditionals in f (x ; :::; x )

n n

1 1 7

whenever p ossible, preserving control dep endencies incurred by the order of conditional tests and

data dep endencies incurred by the bindings. This is done by rep eatedly applying the following trans-

formation in applicative order to the unfolded expression. For any t(e ; :::; e ) b eing c(e ; :::; e ),

1 1

k k

p(e ; :::; e ), f (e ; :::; e ), if e then e else e ,orlet v = e in e ,ife is if e then e else e ,

1 1 1 2 3 1 2 i i1 i2 i3

k k

where i 6=2; 3ift is a conditional, and i 6=2 ore do es not dep end on v if t is a binding expression,

i1

then transform t(e ; :::; e )toif e then t(e ; :::; e ;e ;e ; :::; e ) else t(e ; :::; e ;e ;e ; :::; e ).

1 i1 1 i1 i2 i+1 1 i1 i3 i+1

k k k

This transformation preserves the semantics. It may increase the co de size, but it do es not increase

00 00

the running time of the resulting program. To exploit the conditionals in f (e ; :::; e ), intro duce

n

1

00 00

conditions from f (e ; :::; e ) in the transformed expression just obtained and put function appli-

n

1

cations inside b oth branches that followsuch a condition. This is done by applying the following

transformation in outermost- rst order to the conditionals in the transformed expression just ob-

tained. For each branch e of the conditional that contains a function application, let e be the

i

00 00

outermost condition in f (e ; :::; e ) that is not implied by the context of e ; if e uses only vari-

i

1 n

ables de ned in the context of e and takes constant time to compute, and the two branches in

i

00 00

f (e ; :::; e ) that dep end on e contain di erent function applications in some comp onent, then

1 n

transform e to if e then e else e . To exploit each comp onent in a candidate cache argument

i i i

00 00

r where there is an invariant g (e ; :::; e ) = r , for each branch in the transformed expression,

i i i

i1 in

i

00 00

) under the context of that branch. This may yield additional function ap- sp ecialize g (e ; :::; e

i

i1 in

i

plications that equal various comp onents of r . After these control structures and data structures

i

0 0

are exploited, we simplify primitive op erations on x ; :::; x and transform function applications

n

1

0

recursively based on the four cases describ ed. Finally, after we obtain a de nition of f , replace the

0 0 0 0 0

function application f (e ; :::; e ) with a call to f with arguments e ; :::; e and cache arguments

n n

1 1

e 's for the invariants used.

ir

The simpli cations and equality reasoning needed for all the problems we have encountered

involve only recursive data structures and Presburger arithmetic and can b e fully automated.

0 0

Longest common subsequence. Incrementalize c under hi ;j i = hi +1;ji. We start with

0 0 0 0 0 0

c(i ;j ), with cache argument r and invariant c(pr ev (i ;j )) = c(i 1;j ) = r; the invariants

0 0

i ;j > 0may also b e included but do not a ect any transformation b elow, so they are omitted for

0 0 0

convenience. This is case (4), so weintro duce incremental versionc  to computec (i ;j ). Unfolding

the de nition ofc  and listing conditions to b e exploited, we obtain the co de b elow. The false branch

0 0 0 0

ofc (i ;j ) is duplicated with the additional condition i 1=0 _ j = 0, which is copied from the

0 0

condition in de nition of c(i 1;j ); for convenience, three function applications b ounded to v

1

0 0

to v are not put inside branches that follow condition x[i ]=y [j ], since their transformations are

3

not a ected, and simpli cation at the end can take them backout.

0 0 0 0

c(i ;j )= if i =0 _ j =0 then < 0 >

0 0

else if i 1=0 _ j =0 then

0 0

let v =c(i 1;j 1) in

1

0 0

let v =c(i ;j 1) in

2

0 0

let v =c(i 1;j ) in

3

0 0

if x[i ]= y [j ] then < 1st(v )+1;v ;v ;v >

1 1 2 3

else < max(1st(v ); 1st(v ));v ;v ;v >

2 3 1 2 3

0 0

else let v =c(i 1;j 1) in

1

0 0

let v =c(i ;j 1) in

2

0 0

let v =c(i 1;j ) in

3

0 0

if x[i ]=y [j ] then < 1st(v )+1;v ;v ;v >

1 1 2 3

else < max(1st(v ); 1st(v ));v ;v ;v >

2 3 1 2 3

0 0

In the second branch, i 1 = 0 is true, since j =0 would imply that the rst branchistaken.

The rst and third calls fall in case (1) and sp ecialize to < 0 >. The second call falls in case (3) 8

0 0 0

and equals a recursive call toc  with arguments i ;j 1 and cache argument < 0 > since wehavea

0 0

corresp onding invariant c(i 1;j 1) = < 0 >. Additional simpli cation unwindsbindingsfor v

1

and v , simpli es 1st(< 0 >) + 1 to 1, and simpli es max (1st(v ); 1st(< 0 >)) to 1st(v ).

3 2 2

0 0 0 0

In the third branch, condition i 1=0_ j = 0 is false;c (i 1;j )by de nition ofc  equals its

0 0 0 0 0 0

second branch wherec (i 1;j 1) is b ound to v ,andthusc (i 1;j )=r impliesc (i 1;j 1) =

2

3rd(r ). The rst call falls in case (2) and equals 3rd(r ). The second call falls in case (3) and

0 0 0

equals a recursive call to c with arguments i ;j 1 and cache argument 3rd(r )) since we have a

0 0

corresp onding invariant c(i 1;j 1) = 3rd(r ). The third call falls in case (2) and equals r. We

obtain

0 0 0 0 0

c (i ;j ; r) , if i =0 _ j =0 then < 0 >

0

else if i 1=0 then

0 0 0

let v =c (i ;j 1;< 0 >) in

2

0 0

if x[i ]=y [j ] then < 1;< 0 >; v ;< 0 >>

2

else < 1st(v );< 0 >; v ;< 0 >>

2 2

else let v =3rd(r ) in

1

0 0 0

let v =c (i ;j 1; 3rd(r )) in

2

let v =r in

3

0 0

if x[i ]=y [j ] then < 1st(v )+1;v ;v ;v >

1 1 2 3

else < max(1st(v ); 1st(v ));v ;v ;v >

2 3 1 2 3

0 0 0 0 0 0 0 0 0

Ifr  =c(i 1;j ), thenc  (i ;j ; r)=c (i ;j ), andc  takes time and space linear in j , for caching

and maintaining a linear list.

0 0

Matrix-chain multiplication. Incrementalize m under hi ;j i = hi; j +1i. We start with

0 0 0 0 0 0

m(i ;j ), with cache argument r and invariants m(i ;j 1) = r and i  j . This is case (4),

0 0 0

so we intro duce incremental version m to compute m(i ;j ). Unfolding m, listing conditions, and

sp ecializing the second branch, we obtain the co de b elow.

0 0 0 0

m(i ;j )= if i = j then < 0 >

0 0 0 0 0

else if i = j 1 then ; < 0 >>

0 0 0

else msub(i ;j ;i )

0 0 0 0 0 0

m(i ;j 1) by de nition of m equals msub(i ;j In the third branch, condition i = j 1isfalse;

0 0 0 0 0 0 0 0 0

1;i ), and thus m(i ;j 1) =r  implies msub(i ;j 1;i )=r. The call msub(i ;j ;i ) falls in case

0

00 00 00 00 0 00 0 00 0

(4). We intro duce msub to compute msub(i ;j ;k ) for i = i ;j = j ;k = i , with invariants

0 0 0 0 0 0 0 0 0 0 0

msub(i ;j 1;i ) = r, m(i ;j 1) = r, i  j , i 6= j , i 6= j 1. Express these invariants as

00 00 00

invariants on i ;j ;k using Omega, and intro duce fresh variables r for candidate cache arguments.

i

We obtain

00 00 00 00 00 00 00 00 00 00 00 00 00

msub(i ;j 1;k )=r ; m(i ;j 1) = r ; i  j ; i 6= j ; i 6= j 1; k = i ;

1 2

00 00 00 00 00 00 00 00 00

msub(i ;j 1;i )=r ; k  j ; k 6= j ; k 6= j 1;

3

(2)

00 00 00 00 00

msub(k ;j 1;k )=r ; m(k ;j 1) = r ;

4 5

00 00 00

msub(k ;j 1;i )=r ;

6

00 00

where equation k = i is an additional invariant deduced, and invariants not on the rst line are

00 00 00 00 00

duplications of those in the rst line based on k = i . Arguments of msub(i ;j 1;k ) have a

00 00 00

msub(i ;j ;k ). minimum di erence from arguments of

00 00 00

Unfolding msub(i ;j ;k ) and listing conditions to b e exploited, we obtain the following co de.

00 00

The co de for v and v is duplicated for b oth branches that follow the condition k +1 = j . The

1 2

00 00

co de for v is duplicated for b oth branches that follow the additional condition k +1 = j 1,

00 00 00

msub(i ;j 1;k ). which is copied from the condition in the de nition of 9

00 00 00 00 00

msub(i ;j ;k )= if k +1 = j then

00 00

m(i ;k ) in let v =

1

00 00

let v = m(k +1;j ) in

2

00 00 00

let s =1st(v )+1st(v )+p[i 1]  p[k ]  p[j ] in

1 2

1 2

00 00

else let v = m(i ;k ) in

1

00 00

let v = m(k +1;j ) in

2

00 00 00

let s =1st(v )+1st(v )+ p[i 1]  p[k ]  p[j ] in

1 2

00 00

if k +1 = j 1 then

00 00 00

let v = msub(i ;j ;k +1) in

< min(s; 1st(v ));v ;v ;v >

1 2

00 00 00

else let v = msub(i ;j ;k +1) in

< min(s; 1st(v ));v ;v ;v >

1 2

00 00

The rst branch is simpli ed away since wehave invariant k 6= j 1.

00 00 00 00 00

In the other branch, msub(i ;j 1;k ) by de nition of msub has m(i ;k ) b ound to v and

1

00 00 00 00 00 00 00

m(k +1;j 1) b ound to v ,andthus msub(i ;j 1;k )=r implies m(i ;k )=2nd(r )and

2 1 1

00 00 00 00

m(k +1;j 1) = 3rd(r ). The rst call falls in case (1), since wehaveinvariant k = i , and equals

1

0 00 00

< 0 >. The second call falls in case (3) and equals a recursive call to m with arguments k +1;j

00 00

m(k +1;j 1) = 3rd(r ). and cache argument3rd(r ) since wehave a corresp onding invariant

1 1

00 00

msub falls in case (1) and equals In the branchwherek +1 = j 1 is true, the call to

00 00 00 00

let v = m(i ;j 1) in let v = m(j ;j ) in

1 2

00 00 00

let s =1st(v )+1st(v )+p[i 1]  p[k +1]  p[j ] in

1 2 1 2

00 00 00

which then equals < 1st(r )+p[i 1]  p[k +1] p[j ]; r ;<0 >> b ecause the rst call equals r

2 2 2

and the second call equals < 0 >.

msub falls in case (3). However, the arguments of this call do not In the last branch, the call to

00 00

satisfy the invariant corresp onding to k = i and those on the third and fourth lines in (2). So we

00 00

delete these invariants and retransform msub. Everything remains the same except that m(i ;k )

do es not fall in case (1) any more; it falls in case (2) and equals 2nd(r ). We replace this call to

1

0

00 00 00

msub by a recursive call to msub with arguments i ;j ;k +1 and cache arguments 4th(r ), r ,

1 2

00 00 00 00 00

r since we have corresp onding invariants msub(i ;j 1;k +1) = 4th(r ), m(i ;j 1) = r ,

3 1 2

00 00 00

m(i ;j 1;i )=r .

3

0 0 0

We eliminate unused candidate cache argument r , and we replace the original call msub(i ;j ;i )

3

0 0 0

by msub(i ;j ;i ; r; r). We obtain

0 0 0 0 0

m (i ;j ; r) , if i = j then < 0 >

0 0 0 0 0

else if i = j 1 then ; < 0 >>

0

0 0 0

msub (i ;j ;i ; r; r) else

0

00 00 00

msub (i ;j ;k ; r ; r ) , let v =2nd(r ) in

1 2 1 1

0 00 00

let v = m (k +1;j ; 3rd(r )) in

2 1

00 00 00

let s =1st(v )+1st(v )+p[i 1]  p[k ]  p[j ] in

1 2

00 00

if k +1 = j 1 then

00 00 00

let v = < 1st(r )+p[i 1]  p[k +1] p[j ]; r ;< 0 >> in

2 2

< min(s; 1st(v ));v ;v ;v >

1 2

0

00 00 00

msub (i ;j ;k +1; 4th(r ); r ) in else let v =

1 2

< min(s; 1st(v ));v ;v ;v >

1 2

0 0 0 0 0 0 0 0

m(i ;j 1), then m (i ;j ; r)=m(i ;j ), and m is an exp onential-factor faster. However, Ifr  =

0 0 0

m still takes exp onential time due to rep eated calls to m ; incrementalizing again under hi ;j i =

hi 1;ji, we obtain a linear-time incremental program. 10

5 Step 3: Pruning unnecessary values

0 0 0



Among the comp onents maintained by f (x ; r), the rst one is the return value of f (x ). Com-

0 0

p onents in r that are not useful for computing this value need not b e cached and maintained. We

0

  ^

prune the programs f and f and obtain a program f that caches only the useful values and a

0 0 0

0

^

program f that uses and maintains only the useful values. Finally,we form an optimized program

0

0

^ ^

that computes f by using the base cases in f and by rep eatedly using the incremental version f .

0 0 0

Pruning. Pruning requires a dep endence analysis that can precisely describ e substructures of

recursive trees [30 ]. We use an analysis metho d based on regular tree grammars [26 ]. We have

implemented a simpli ed version that uses set constraints to eciently pro duce precise analysis

results. Pruning can save space, as well as time, and reduce co de size.

0

For example, in program c , only the third comp onent of r is useful. Pruning the second

0

and fourth comp onents of c and c , which moves the third up to the second, and doing a few

simpli cations, which transforms 1st(c)backto c and unwinds bindings for v and v ,we obtainc ^

1 3

0

and c^ b elow:

c^(i; j ) , if i =0 _ j =0 then < 0 >

else let v =^c(i; j 1) in

2

if x[i]=y [j ] then

2

else < max(1st(v );c(i 1;j)));v >

2 2

0 0 0 0 0

c^ (i ;j ; r^) , if i =0 _ j =0 then < 0 >

0

else if i 1=0 then

0 0 0

let v =^c (i ;j 1;< 0 >) in

2

0 0

if x[i ]=y [j ] then < 1;v >

2

else < 1st(v );v >

2 2

0 0 0

else let v =^c (i ;j 1; 2nd(^r )) in

2

0 0

if x[i ]=y [j ] then < 1st(2nd(^r )) + 1;v >

2

else < max(1st(v ); 1st(^r ));v >

2 2

0 0

b b

Pruning leaves programs m and m unchanged. We obtain the same programs m and m , resp ec-

tively.

0

^ ^

: Forming optimized programs. We rede ne functions f and f and use function f

0 0 0

0

^ ^ ^ ^

cond(x) then base val(x) else let r^ = f (pr ev (x)) in f (x; r^) f (x) , 1st(f (x)) f (x) , if base

0 0 0 0 0

where base cond is the base-case condition, and base val is the corresp onding value, both copied

^

from the de nition of f . In general, there maybemultiple base cases, and we just list them all.

0

For examples c and m,we obtain directly

0

c(i; j ) , 1st(^c(i; j )) c^(i; j ) , if i =0 _ j =0 then < 0 > else let r^ =^c(i 1;j) in c^ (i; j; r)

0

m(i; j ) , 1st(mb (i; j )) mb (i; j ) , if i = j then < 0 > else let r^ = mb (i; j 1) in mb (i; j; r)

0 0 n+m

b

wherec ^ and m are as obtained ab ove. For c(n; m), while the original program takes O (2 ) time,

n

the optimized program takes O (n  m)time. For m(1;n), while the original program takes O (n  3 )

2 n

time, the optimized program takes O (n  2 )time. Incrementalizing the optimized program again

3

under the increment to the other parameter, we obtain an optimized program that takes O (n )

time. 11

6 Summary and discussion

Our metho d for dynamic programming is completely static, fully automatable, and ecient. In

particular, it is based on a general approach for |incrementalization. Al-

though our static incrementalization allows only one incremental version for each original function,

it is still p owerful enough to incrementalize all examples in [31 , 30 , 29 ], including various list ma-

nipulations, matrix computations, attribute evaluation, and graph problems. We b elieve that our

metho d can p erform dynamic programming for all problems whose solutions involve recursively

solving subproblems that overlap, but a formal justi cation awaits more rigorous study.

In our metho d, only values that are necessary for the incrementalization are stored, in appro-

priate data structures. For the longest-common-subsequence example, only a linear list is needed,

whereas in standard textb o oks, a quadratic two-dimensional array is used, and an additional opti-

mization is needed to reduce it to a one-dimensional array [13 ]. For the matrix-chain-multiplication

example, our optimized program uses a list of lists that forms a triangle shap e, rather than a two-

dimensional array of square shap e. It's nontrivial to see that recursive data structures gives the

same asymptotic sp eedup as arrays for these examples. There are dynamic programming problems,

e.g., 0-1 knapsack, that require the use of array, with constant-time access of elements, to achieve

the desired asymptotic sp eedup. Such need falls out explicitly when doing incrementalization and

can be taken care of easily. This will be describ ed in a future pap er. Although we present the

optimizations for a functional language, the underlying principle is general and has been applied

to programs that use lo ops and arrays [25 , 28 ].

Some values computed in a hoisted program might not be computed by the original program

and are therefore called auxiliary information [29 ]. Both incrementalization and pruning pro duce

programs that are as least as fast as the given program, but caching auxiliary information may

result in a slower program on certain inputs. We can determine statically whether such information

is cached in the nal program. If so, we can use time and space analysis [27 ] to determine whether

it is worthwhile to use and maintain such information.

Many dynamic programming algorithms can be further improved by exploiting greedy prop-

erties of the given problems [7 ]. Our metho d is not sp ecially aimed at discovering such greedy

prop erties. Nevertheless, it can maintain such prop erties once they are added. For example, for

the paragraph-formatting problem [13 , 16 ], we can derive a quadratic-time algorithm that uses

dynamic programming; if the original program has a simple extra condition that follows from a

greedy prop erty, our derived dynamic programming program uses it as well and takes linear time

with a factor of line width.

7 Implementation and exp erimentation results

All three steps have b een implemented in a prototyp e system, CACHET. The incrementalization

step is semi-automatic [24 ] and is currently b eing automated. The implementation uses the Syn-

thesizer Generator [45 ].

Figure 3 summarizes some of the examples derived (most of them semi-automatically and some

automatically) and compares their asymptotic running times. The second column shows whether

more than one cache argument is needed in an incremental program. The third column shows

whether the incremental program computes values not necessarily computed by the original pro-

gram. Paragraph formatting 2 [16 ] includes a conditional that re ects a greedy prop erty. The

\a" in the third column for the last two examples shows that cached values are stored in arrays.

Performance measurements con rmed drastic sp eedups. 12

multiple aux original program's optimized prog's

Examples

cache arg info running time running time

n

Fib onacci function [37 ] O (2 ) O (n)

n

binomial co ecients [37 ] O (2 ) O (n  k )

p

n+m

longest common subsequence [13 ] O (2 ) O (n  m)

p

n 3

matrix-chain multiplication [13 ] O (n  3 ) O (n )

n+m

string editing distance [44 ] O (3 ) O (n  m)

p

n 2

dag path sequence [6 ] O (2 ) O (n )

p

n 3

optimal p olygon triangulation [13 ] O (n  3 ) O (n )

p

n 3

optimal binary search trees [2 ] O (n  3 ) O (n )

p

n 2

paragraph formatting [13 ] O (n  2 ) O (n )

p

n

paragraph formatting 2 O (n  2 ) O (n  w idth)

p

n

0-1 knapsack [13 ] a O (2 ) O (n  w eig ht)

p p

n 3

context-free-grammar [2 ] O (n  (2  siz e +1) ) O (n  siz e) a

Figure 3: Summary of Examples.

8 Related work and conclusion

Dynamic programming was rst formulated by Bellman [4 ] and has been studied extensively

since [48 ]. Bird [5 ], de Mo or [15 ], and others have studied it in the context of program transforma-

tion. These previous metho ds either apply to sp eci c sub classes of problems [12 , 38 , 9, 11 , 40 , 20 ]

or give general frameworks and strategies rather than precise algorithms [49 , 8, 5, 46 , 6, 3, 37 , 47 ,

15 , 39, 14 ]. Our work is based on the general principle of incrementalization [36 , 29 ] and consists

of precise analyses and transformations.

In particular, tupling [38 , 39 ] aims to compute multiple values together in an ecient way. It

is improved to b e automatic on sub classes of problems [9 ] and to work on more general forms [11 ].

It is also extended to store lists of values [40 ], but such lists are generated in a xed way, whichis

not the most appropriate way for many programs. A sp ecial form of tupling can eliminate multiple

data traversals for many functions [20 ]. A metho d sp ecialized for intro ducing arrays was prop osed

for tabulation [10 ], but as our metho d has shown, array is not essential for the sp eedup of many

programs; their arrays are complicated to derive and often consume more space than necessary.

Compared with our previous work for incrementalizing functional programs [31 , 30 , 29 ], this

work contains drastic improvements. First, our previous work address the systematic derivation

0

given both program f and op eration . This pap er describ es a of an incremental program f

systematic metho d for identifying an appropriate op eration  given a function f and using the

0

derived incremental program f to form an optimized version of f . Second, since it is dicult to

intro duce appropriate cache arguments, our previous metho d allows at most one cache argument

for each incremental function. This pap er allows multiple cache arguments, without which many

programs could not b e incrementalized, e.g., the matrix-chain-multiplication program. Third, our

previous metho d intro duces incremental functions using an on-line strategy, i.e., on-the- y during

the transformation, so it may attempt to intro duce an unb ounded numb er of new functions and thus

not terminate. The algorithm in this pap er statically determines one incremental function for each

one in the original program, i.e., it is monovariant; even though it is theoretically more limited, it

is simpler, always terminates, and is able to incrementalize all previous examples. Finally, based on

the idea of cache-and-prune that was prop osed earlier [30 ], the metho d in this pap er uses hoisting

to extend the set of intermediate results [30 ] to include a kind of auxiliary information [29 ] that

is sucient for dynamic programming. This metho d is simpler than our previous general metho d 13

for discovering auxiliary information [29 ]. Additionally, we now use a more precise and ecient

dep endence analysis for pruning [26 ].

Finite di erencing [36 , 35 ] is based on the same underlying principle as incremental computa-

tion. Paige has explicitly asked whether nite di erencing can be generalized to handle dynamic

programming [34 ]; it is clear that he p erceived an imp ortant connection. However, nite di er-

encing has been formulated for set-based languages, while straightforward solutions to dynamic

programming problems are usually formulated as recursive functions, so it was dicult to actually

establish the connection.

Overall, being able to incrementalize complicated recursion in a systematic way is a more

drastic improvement complementing previous metho ds for incrementalizing lo ops [36 , 25 ]. Our new

metho d based on static incrementalization is general and fully automatable. Based on our existing

implementation, we b elieve that a complete system will p erform incrementalization eciently.

References

[1] M. Abadi, B. Lampson, and J.-J. Levy. Analysis and caching of dep endencies. In Proceedings of the 1996 ACM

SIGPLAN International ConferenceonFunctional Programming.ACM, New York, May 1996.

[2] A. V. Aho, J. E. Hop croft, and J. D. Ullman. The Design and Analysis of Computer Algorithms. Addison-Wesley,

Reading, Mass., 1974.

[3] F. L. Bauer, B. Moller, H. Partsch, and P.Pepp er. Formal program construction by transformations|Computer-

aided, intuition-guided programming. IEEE Trans. Softw. Eng., 15(2):165{180, Feb. 1989.

[4] R. E. Bellman. Dynamic Programming. Princeton University Press, Princeton, New Jersey, 1957.

[5] R. S. Bird. Tabulation techniques for recursive programs. ACM Comput. Surv., 12(4):403{417, Dec. 1980.

[6] R. S. Bird. The promotion and accumulation strategies in transformational programming. ACM Trans. Program.

Lang. Syst., 6(4):487{504, Oct. 1984.

[7] R. S. Bird and O. de Mo or. From dynamic programming to greedy algorithms. In B. Moller, H. Partsch, and

S. Schuman, editors, Formal Program Development, volume 755 of Lecture Notes in Computer Science, pages

43{61. Springer-Verlag, Berlin, 1993.

[8] R. M. Burstall and J. Darlington. A transformation system for developing recursive programs. J. ACM, 24(1):44{

67, Jan. 1977.

[9] W.-N. Chin. Towards an automated tupling strategy. In Proceedings of the ACM SIGPLAN Symposium on

Partial Evaluation and Semantics-BasedProgram Manipulation, pages 119{132. ACM, New York, June 1993.

[10] W.-N. Chin and M. Hagiya. A b ounds inference metho d for vector-based memoization. In ICFP 1997 [22 ], pages

176{187.

[11] W.-N. Chin and S.-C. Kho o. Tupling functions with multiple recursion parameters. In P. Cousot, M. Falaschi,

G. File, and A. Rauzy, editors, Proceedings of the 3rd International Workshop on Static Analysis,volume 724

of Lecture Notes in Computer Science, pages 124{140. Springer-Verlag, Berlin, Sept. 1993.

[12] N. H. Cohen. Eliminating redundant recursivecalls. ACM Trans. Program. Lang. Syst., 5(3):265{299, July 1983.

[13] T. H. Cormen, C. E. Leiserson, and R. L. Rivest. Introduction to Algorithms. The MIT Press/McGraw-Hill,

1990.

[14] S. Curtis. Dynamic programming: A di erent p ersp ective. In R. Bird and L. Meertens, editors, Algorithmic

Languages and Calculi, pages 1{23. Chapman & Hall, London, U.K., 1997.

[15] O. de Mo or. A generic program for sequential decision pro cesses. In M. Hermenegildo and D. S. Swierstra, editors,

Programming Languages: Implementations, Logics, and Programs, volume 982 of Lecture Notes in Computer

Science, pages 1{23. Springer-Verlag, Berlin, 1995.

[16] O. de Mo or and J. Gibb ons. Bridging the algorithm gap: A linear-time functional program for paragraph

formatting. Technical Rep ort CMS-TR-97-03, Scho ol of Computing and Mathematical Sciences, Oxford Bro okes

University, July 1997.

[17] J. Field and T. Teitelbaum. Incremental reduction in the lamb da calculus. In Proceedings of the 1990 ACM

Conference on LISP and , pages 307{322. ACM, New York, June 1990.

[18] D. P.Friedman, D. S. Wise, and M. Wand. Recursive programming through table lo ok-up. In Proceedings of

the 1976 ACM Symposium on Symbolic and Algebraic Computation, pages 85{89. ACM, New York, 1976.

[19] Y. Futamura and K. Nogi. Generalized partial evaluation. In B. Bjrner, A. P. Ershov, and N. D. Jones, editors,

Partial Evaluation and Mixed Computation, pages 133{151. North-Holland, Amsterdam, 1988. 14

[20] Z. Hu, H. Iwasaki, M. Takeichi, and A. Takano. Tupling calculation eliminates multiple data traversals. In ICFP

1997 [22 ], pages 164{175.

[21] J. Hughes. Lazy memo-functions. In Proceedings of the 2nd ConferenceonFunctional Programming Languages

and Computer Architecture,volume 201 of Lecture Notes in Computer Science, pages 129{146. Springer-Verlag,

Berlin, Sept. 1985.

[22] Proceedings of the 1997 ACM SIGPLAN International ConferenceonFunctional Programming.ACM, New York,

June 1997.

[23] R. M. Keller and M. R. Sleep. Applicativecaching. ACM Trans. Program. Lang. Syst., 8(1):88{108, Jan. 1986.

[24] Y. A. Liu. CACHET: An interactive, incremental-attribution-based program transformation system for deriving

incremental programs. In Proceedings of the 10th Know ledge-Based Software Engineering Conference, pages

19{26. IEEE CS Press, Los Alamitos, Calif., Nov. 1995.

[25] Y. A. Liu. Principled strength reduction. In R. Bird and L. Meertens, editors, Algorithmic Languages and

Calculi, pages 357{381. Chapman & Hall, London, U.K., 1997.

[26] Y. A. Liu. Dep endence analysis for recursive data. In Proceedings of the IEEE 1998 International Conference

on Computer Languages, pages 206{215. IEEE CS Press, Los Alamitos, Calif., May 1998.

[27] Y. A. Liu and G. Gomez. Automatic accurate time-b ound analysis for high-level languages. In Proceedings of

the ACM SIGPLAN 1998 Workshop on Languages, Compilers, and Tools for Embedded Systems,volume 1474

of Lecture Notes in Computer Science. Springer-Verlag, June 1998.

[28] Y. A. Liu and S. D. Stoller. Lo op optimization for aggregate array computations. In Proceedings of the IEEE

1998 International Conference on Computer Languages, pages 262{271. IEEE CS Press, Los Alamitos, Calif.,

May 1998.

[29] Y. A. Liu, S. D. Stoller, and T. Teitelbaum. Discovering auxiliary information for incremental computation.

In Conference Record of the 23rd Annual ACM Symposium on Principles of Programming Languages, pages

157{170. ACM, New York, Jan. 1996.

[30] Y. A. Liu, S. D. Stoller, and T. Teitelbaum. Static caching for incremental computation. ACM Trans. Program.

Lang. Syst., 20(3):546{585, May 1998.

[31] Y. A. Liu and T. Teitelbaum. Systematic derivation of incremental programs. Sci. Comput. Program., 24(1):1{39,

Feb. 1995.

[32] D. Michie. \memo" functions and . Nature, 218:19{22, Apr. 1968.

[33] D. J. Mostow and D. Cohen. Automating program sp eedup by deciding what to cache. In Proceedings of the

9th International Joint ConferenceonArti cial Intel ligence, pages 165{172. Morgan Kaufmann Publishers, San

Francisco, Calif., Aug. 1985.

[34] R. Paige. Programming with invariants. IEEE Software, pages 56{69, Jan. 1986.

[35] R. Paige. Symb olic nite di erencing|Part I. In Proceedings of the 3rdEuropean Symposium on Programming,

volume 432 of Lecture Notes in Computer Science, pages 36{56. Springer-Verlag, Berlin, May 1990.

[36] R. Paige and S. Ko enig. Finite di erencing of computable expressions. ACM Trans. Program. Lang. Syst.,

4(3):402{454, July 1982.

[37] H. A. Partsch. Speci cation and Transformation of Programs|A Formal Approach to Software Development.

Springer-Verlag, Berlin, 1990.

[38] A. Pettorossi. Apowerful strategy for deriving ecient programs by transformation. In ConferenceRecordof

the 1984 ACM Symposium on LISP and Functional Programming.ACM, New York, Aug. 1984.

[39] A. Pettorossi and M. Proietti. Rules and strategies for transforming functional and logic programs. ACM

Comput. Surv., 28(2):360{414, June 1996.

[40] A. Pettorossi and M. Proietti. Program derivation via list intro duction. In R. Bird and L. Meertens, editors,

Algorithmic Languages and Calculi. Chapman & Hall, London, U.K., 1997.

[41] W. Pugh. An improved cache replacement strategy for function caching. In Proceedings of the 1988 ACM

Conference on LISP and Functional Programming, pages 269{276. ACM, New York, July 1988.

[42] W. Pugh. The Omega Test: A fast and practical algorithm for dep endence analysis.

Commun. ACM, 31(8), Aug. 1992.

[43] W. Pugh and T. Teitelbaum. Incremental computation via function caching. In ConferenceRecord of the 16th

Annual ACM Symposium on Principles of Programming Languages, pages 315{328. ACM, New York, Jan. 1989.

[44] P. W. Purdom and C. A. Brown. The Analysis of Algorithms. Holt, Rinehart and Winston, 1985.

[45] T. Reps and T. Teitelbaum. The Synthesizer Generator: A System for Constructing Language-Based Editors.

Springer-Verlag, New York, 1988.

[46] W. L. Scherlis. Program improvementbyinternal sp ecialization. In ConferenceRecord of the 8th Annual ACM

Symposium on Principles of Programming Languages, pages 41{49. ACM, New York, Jan. 1981.

[47] D. R. Smith. KIDS: A semiautomatic program development system. IEEE Trans. Softw. Eng., 16(9):1024{1043,

Sept. 1990.

[48] M. Sniedovich. Dynamic Programming. Marcel Dekker, New York, 1992.

[49] B. Wegbreit. Goal-directed program transformation. IEEE Trans. Softw. Eng., SE-2(2):69{80, June 1976. 15