300 likes | 405 Views
Fundamentals of. Staged Computation. Tim Sheard Oregon Graduate Institute. Lecture 3: More Examples. CSE 510 Section FSC Winter 2004. Assignments. The paper DSL Implementation Using Staging and Monads is now officially assigned. See the web page to obtain a copy.
E N D
Fundamentals of Staged Computation Tim Sheard Oregon Graduate Institute Lecture 3: More Examples CSE 510 Section FSC Winter 2004
Assignments • The paper DSL Implementation Using Staging and Monads is now officially assigned. See the web page to obtain a copy. • A volunteer presenter is needed to present the paper next Monday. • Homework 2, due in one week, is now assigned. See webpage for details • Due in one week, Monday Jan 18 Cse583 Winterl 2002
An Interesting Note • Some functions are code transformers • plus : int -> <int> -> <int> • plus x ycode = < ~(lift x) + ~ycode > • Others are code generators • plus' : int -> <int -> int> • plus' x = <fn y => ~(lift x) + y> • When do we use one? When do we use the other? Are they equivalent? Cse583 Winterl 2002
Transformers to generators • Code transformers can be made code generators with a “trick” fun plus’’ n = <fn y => ~(plus n <y>)>; -| plus’’ 3; val it = <(fn a = 3 %+ a)> : <int -> int> Cse583 Winterl 2002
Another example: two versions fun add3 0 y = y | add3 n y = < 1 + ~(add3 (n-1) y)>; fun add4 0 = <fn y => y> | add4 n = <fn y => 1 + ~(add4 (n-1)) y>; Cse583 Winterl 2002
Exponentially large generated programs fun even x = (x mod 2) = 0; fun filter p [] = <[]> | filter p (x::xs) = <if ~p ~(lift x) then ~(filter p xs) else ~(lift x) :: ~(filter p xs)>; -| filter <even> [3]; val it = <if %even 3 then [] else [3]> : <int list> Note the repeated, related (here identical) , recursive calls Cse583 Winterl 2002
But let the list grow -| filter <even> [1,2,3,4]; val it = <if %even 1 then if %even 2 then if %even 3 then if %even 4 then [] else [4] else 3 :: if %even 4 then [] else [4] else 2 :: if %even 3 then if %even 4 then [] else [4] else 3 :: if %even 4 then [] else [4] else 1 :: if %even 2 then if %even 3 then if %even 4 then [] else [4] else 3 :: if %even 4 then [] else [4] else 2 :: if %even 3 then if %even 4 then [] else [4] else 3 :: if %even 4 then [] else [4]> : <int list> Cse583 Winterl 2002
Use let to share calls fun filter p [] = <[]> | filter p (x::xs) = <let val ys = ~(filter p xs) in if ~p ~(lift x) then ys else ~(lift x) :: ys end>; val ex2 = filter <even> [1,2,3,4]; -| val ex2 = <let val a = [] val b = if %even 4 then a else 4 :: a val c = if %even 3 then b else 3 :: b val d = if %even 2 then c else 2 :: c in if %even 1 then d else 1 :: d end> Cse583 Winterl 2002
Staged Shortest Path • Given a directed weighted graph G=(V,E) • Two nodes a,b in V, • The shortest path algorithm finds the minimum weighted path in G from the source a to the destination b. • Where the weights are given by a weight function W Cse583 Winterl 2002
3 5 2 1 4 Representing Graphs fun g x = case x of 1 => [2] | 2 => [3,4] | 3 => [5] | 4 => [1,2,5] | 5 => [4] | _ => []; Cse583 Winterl 2002
Structure of function • fun shortestPath succ source dest marked weight = . . . • succ is the function associating to a vertex a list of successor vertexes, and encodes the topology of the graph • source is the source vertex; • dest is the destination vertex; • marked indicates which nodes have already been visited by the algorithm; • (weight x y) indicates the weight of the edge (x,y). Cse583 Winterl 2002
Helper functions (* min : int -> int -> int *) fun min x z = if x '<' z then x else z ; (* minimum : int list -> int *) fun minimum (x::xs) = (case xs of [] => x | _ => min x (minimum xs)); (* map : ('b -> 'a) -> 'b list -> 'a list *) fun map f [] = [] | map f (a::b) = (f a)::(map f b); (* mem : int -> int list -> bool *) fun mem x [] = false | mem x (a::b) = if a=x then true else mem x b; (* minus : int list -> int list -> int list *) fun minus [] ys = [] | minus (a::b) ys = if mem a ys then minus b ys else a::(minus b ys); Cse583 Winterl 2002
shortestPath fun shortestPath succ source dest marked weight = if source = dest then 0 else let val marked2 = (source::marked) val explore = (minus (succ source) marked2) fun short x = shortestPath succ x dest marked2 weight val path_weights = map (fn node => (short node) + (weight source node)) explore in case path_weights of [] => infinity | _ => (minimum path_weights) end; Cse583 Winterl 2002
3 5 2 1 4 Example • val ex1 = let fun weight x y = 1 in shortestPath g 3 2 [] weight end; -| val ex1 = 3 : int Cse583 Winterl 2002
Staging shortestPath • The graph is known • The source and destination are known • The weight function is dynamic and won’t be known until later. shortestPath : graph -> int -> int -> int list -> (int -> int -> int) -> int shortestPath2 : graph -> int -> int -> int list -> <(int -> int -> int) -> int> Cse583 Winterl 2002
Stage helper functions • Staging the minimum function liftMinimum : <int> list -> <int> fun liftMinimum x = case x of [] => <infinity> | (c::cs) => <min ~c ~(liftMinimum cs)>; Cse583 Winterl 2002
shortestPath2 fun shortestPath2 succ source dest marked = <fn weight => ~(if source = dest then <0> else let val marked2 = source::marked val explore = (minus (succ source) marked) fun short x = shortestPath2 succ x dest marked2 val path_weights = map (fn node => < (~(short node) weight) + (weight ~(lift source) ~(lift node)) > ) explore in (liftMinimum path_weights) end ) >; Note shortestPath2 is in generator style Cse583 Winterl 2002
3 5 2 1 4 Results -| val ex2 = (shortestPath2 g 4 2 []); val ex2 = <(fn a => %min (%min (0 %+ a 1 2) (%infinity %+ a 4 1)) (%min (0 %+ a 4 2) (%min (%infinity %+ a 4 5) %infinity)))> : <(int -> int -> int) -> int> Cse583 Winterl 2002
Unnest the calls to min fun liftMinimum x = case x of [] => <infinity> | [x] => x | ( <infinity> :: cs) => liftMinimum cs | (c::cs) => <let val y = ~(liftMinimum cs) val z = ~c in min z y end>; Note special case for singleton list Cse583 Winterl 2002
shortestPath2 again fun shortestPath2 succ source dest marked = <fn weight => ~(if source = dest then <0> else let val marked2 = source::marked val explore = (minus (succ source) marked) fun short x = shortestPath2 succ x dest marked2 val path_weights = map (fn node => let val recall = < ~(short node) weight> val wght = <weight ~(lift source) ~(lift node)> in <let val q = ~recall in q + ~wght end> end) explore in (liftMinimum path_weights) end ) >; Note the generated let Cse583 Winterl 2002
3 5 2 1 4 Results val ex2 = (shortestPath2 g 4 2 []); -| val ex2 = <(fn a => let val b = %infinity %+ a 4 5 val c = 0 %+ a 4 2 val d = %min c b val e = 0 %+ a 1 2 val f = e %+ a 4 1 in %min f d end)> : <(int -> int -> int) -> int> Cse583 Winterl 2002
Extended example 2 • Staged merge function for sorted lists • Un-staged version fun merge xs ys = case xs of [] => ys | (z::zs) => case ys of [] => xs | w::ws => if z '<' (w:int) then z::(merge zs ys) else w::(merge xs ws); Suppose xs is static and ys is dynamic Cse583 Winterl 2002
First staged attempt fun merge2 xs = <fn ys => ~(case xs of [] => <ys> | (z::zs) => <case ys of [] => ~(lift xs) | (w::ws) => if z '<' (w:int) then z::(~(merge2 zs) ys) else w::(~(merge2 xs) ws) >)>; Goes into an infinite loop. Why? Cse583 Winterl 2002
Second Attempt • First some helper functions fun reverse [] ys = ys | reverse (x::xs) ys = (reverse xs (x::ys)); fun split2 (n:int) xs (small,big) = case xs of [] => (reverse small [], reverse big []) | (z::zs) => if z '>' n then split2 n zs (small,z::big) else split2 n zs (z::small,big); fun split n l = (split2 n l ([],[])); Cse583 Winterl 2002
Unstaged version using the helper functions fun merge3 xs ys = case xs of [] => ys | z::zs => (case ys of [] => xs | w::ws => if z '<' w then z::(merge3 zs ys) else let val (u,v) = split z ys in append u (z::(merge3 zs v)) end ); u is all elements of ys less than z split avoids recursive call on xs Cse583 Winterl 2002
Staged Version fun merge4 xs = <fn ys => ~(case xs of [] => <ys> | (b::bs) => <case ys of [] => ~(lift xs) | (w::ws) => if (~(lift b)) '<' w then (~(lift b))::(~(merge4 bs) ys) else let val (low,high) = split (~(lift b)) ys in append low (~(lift b)::(~(merge4 bs) high)) end >) >; Cse583 Winterl 2002
Not so-good, Generated code -| merge4 [2,5]; val it = <(fn a => (case a of [] => [2,5] | (c::b) => if 2 %'<' c then 2 :: (case a of [] => [5] | (k::j) => if 5 %'<' k then 5 :: a else let val (m,l) = %split 5 a in %append m (5 :: l) end) else let val (e,d) = %split 2 a in %append e (2 :: (case d of [] => [5] | (g::f) => if 5 %'<' g then 5 :: d else let val (i,h) = %split 5 d in %append i (5 :: h) end)) end))> : <int list -> int list> Cse583 Winterl 2002
Improve by • use transformer style • use let to share recursive call fun merge5 xs ys = case xs of [] => ys | (b::bs) => <case ~ys of [] => ~(lift xs) | (w::ws) => let val tail = ~(merge5 bs ys) val (low,high) = split (~(lift b)) ~ys in if (~(lift b)) '<' w then (~(lift b)):: tail else append low (~(lift b):: tail) end >; fun f1 xs = <fn ys => ~(merge5 xs <ys>)>; Cse583 Winterl 2002
Better code -| f1 [2,3]; val it = <(fn a => (case a of [] => [2,3] | (c::b) => let val d = case a of [] => [3] | (f::e) => let val (h,g) = %split 3 a in if 3 %'<' f then 3 :: a else %append h (3 :: a) end) val (j,i) = %split 2 a in if 2 %'<' c then 2 :: d else %append j (2 :: d) end))> Cse583 Winterl 2002
Review • Multiple stage programs require nested brackets. Transformer style is often useful. • Staged helper functions are often necessary. • Generated programs must follow all paths of a generated if or case, even if the unstaged version will follow only one path. • Leads to possiblility of exponentially growing code. • Can cause the generator not to terminate if recursive call at first level doesn’t “get smaller”, even though it gets smaller (in the second stage) in the unstaged program. • Use let to share similar or related code to avoid this blowup. • Use let to avoid deeply nested function calls. Simplifies the structure, and won’t blow up the object level compiler by generating code in weird ways. Cse583 Winterl 2002