1 / 40

Next Steps in Partial Program Synthesis

Next Steps in Partial Program Synthesis. Rastislav Bodik Shaon Barman, Joel Galenson, Casey Rodarmor, Nicholas Tung Satish Chandra*, Doug Kimelman *, Emina Torlak* University of California, Berkeley *IBM Research ParLab Retreat, Jan 2010. How should synthesis help?.

amarietta
Download Presentation

Next Steps in Partial Program Synthesis

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Next Steps in Partial Program Synthesis Rastislav Bodik Shaon Barman, Joel Galenson, Casey Rodarmor, Nicholas TungSatish Chandra*, Doug Kimelman*, Emina Torlak* University of California, Berkeley *IBM Research ParLab Retreat, Jan 2010

  2. How should synthesis help? Program synthesis moves formal systems into early stages of program development. Verification is a back-end activity: quality assurance. Synthesis is a front-end activity: ???. Synthesis: find problem decomposition & invariants. Algorithms are too diverse to be encoded in a library domain theory. Which means that the programmer must be able to program the synthesizer.

  3. Partial Programs originated in aLisp [Andre, Russell, et al, 2002] if (choose) collect wood else build barracks … switch (choose): North: go North South: go South

  4. Synthesis from Partial Programs Part. program P: defines a space of candidate programs A parameter c determines the candidate P[c] Synthesizer picks a candidate that meets the spec:  parameter c .  inputs x . safeP[c](x) Examples: P1: assert x << ?? == x*2 P2: for (i = ?? to linExpr(N,I,??)) …

  5. verifier/checker Your verifier/checker goes here Synthesizer: CEGIS The CounterExample –Guided Inductive Synthesis algorithm: candidate implementation SAT • Inductive Synthesizer compute candidate implementation from concrete inputs. UNSAT ok buggy fail observation set E add a (bounded) counterexample input Inductive synthesis step implemented with a SAT solver

  6. Synthesizer: Convergence Testing succeed a correct candidate Two candidate implementations c1, c2 + distinguishing input x c1(x)≠c2(x) • Inductive Synthesizer Evaluate spec on x compute candidate implementation from concrete inputs. fail buggy observation set E add an input-output pair (x,spec(x)) [Jha, Gulwani, Seshia, Tiwari, ICSE 2010]

  7. Partial programs: benefits Often conveys insight, decoupling it from mechanics Replaces rewrite rules: says how the program should look like, not how to derive it Which allows programmers to program the synthesizer

  8. Who writes partial programs programmers, one p.program per problem • sketch [Solar-Lezama, Bodik, Seshia ASPLOS 2006 ] • skeleton [Srivastava, Foster, Gulwani POPL 2010] library writers, one for a class of problems • target language[Itzhaky, Gulwani, Immerman, Sagiv, SPLASH 2010] generated from program being repaired • program repair with genetic programming [Weimer et al, 2009]

  9. Partial programs: limitations Cannot encode vague insight. Sometimes to come up with the partial program is as hard as coming up with the full program Q1: How to free ourselves from the syntactic bind? Q2: How to entirely hide the partial program?

  10. Interactive synthesis

  11. What's your memory of Red-Black Tree? left_rotate( Tree T, node x ) { node y; y = x->right; /* Turn y's left sub-tree into x's right sub-tree */ x->right = y->left; if ( y->left != NULL ) y->left->parent = x; /* y's new parent was x's parent */ y->parent = x->parent; /* Set the parent to point to y instead of x */ /* First see whether we're at the root */ if ( x->parent == NULL ) T->root = y; else if ( x == (x->parent)->left ) /* x was on the left of its parent */ x->parent->left = y; else /* x must have been on the right */ x->parent->right = y; /* Finally, put x on y's left */ y->left = x; x->parent = y; } http://www.cs.auckland.ac.nz/software/AlgAnim/red_black.html

  12. Jim Demmel's napkin

  13. Programmers often think with examples They often design algorithms by devising and studying examples demonstrating steps of algorithm at hand. If only the programmer could ask for a demonstration of the desired algorithm! The demonstration (a trace) would reveal the insight. Could an executable oracle demonstrate it for him?

  14. Demonstrations by an oracle Imagine we want to know whether to reverse a list: We ask an oracle for a demonstration: We then mimic the oracle with a deterministic program. How do we instruct the oracle what to demonstrate? ? 1 2 3 3 2 1 1 2 3

  15. An angelic program • Operator !! evaluates to a value yielding a safe trace. • reverse(list) { • while (!!) { • !!(Node).next =!!(Node) • } • reversedList =!!(Node) • assert reversedList is reversal of list • return reversedList • } • Each trace of an angelic program is a demonstration. a Boolean value pointer to an existing object of type Node correctness check

  16. Angelic choice Angelic nondeterminism embedded into Scala. Oracle makes an angelic (clairvoyant) choice. !!(S) evaluates to a value chosen from set S such that the execution terminates without violating an assertion We developed two implementations of oracle: • parallel backtracking • SAT solver (reduction to SKETCH language)

  17. First case study Design DFS traversal that does not use a stack. Used in garbage collection: when out of memory, you cannot ask for O(N) memory to mark reachable nodes We want DFS that uses O(1) memory.

  18. Depth-first search with explicit stack vroot = new Node(g.root) push(vroot); current = g.root while (current != vroot) { if (!current.visited) current.visited = true if (current has unvisited children) { current.idx := index of first unvisited child child = current.children[current.idx] push(current) current = child } else { current = pop() } Node idx children

  19. Parasitic Stack Borrows storage from its host (the graph) accesses the host graph via pointers present in traversal code A two-part interface: stack: usual push and pop semantics parasitic channel: for borrowing/returning storage push(x,(node1,node2,…)) stack can (try to) borrow fields in nodei pop(node1,node2,…)value nodeimay be handy in returning storage Parasitic stack expresses an optimization idea But can DSW be modularized this way? Angels will tell us.

  20. Replace regular stack with parasitic stack vroot = new Node(root) push(null); current = vroot while (current != vroot) { if (!current.visited) current.visited = true if (current has unvisited children) { current.idx := index of first unvisited child child = current.children[current.idx] push(current, (current, child)) current = child } else { current = pop((current)) } Node idx idx idx children children children

  21. Angels perform deep global reasoning Which location to borrow? traversal must not need it until it is returned How to restore the value in the borrowed location? the stack does not have enough locations to remember it How to use the borrowed location? it must implement a stack Angels will clairvoyantly made these decisions for us • in principle, human could set up this parasitic “wiring”, too, but we failed without the help of the angels

  22. ParasiticStack.push class ParasiticStack { var e // allow ourselves one extra storage location push(x, nodes) { // borrow memory location n.children[c] n = !!(nodes) c = !!(0 until n.children.length) // value in the borrowed location; will need to be restored v = n.children[c] // we are holding 4 values but have only 2 memory locations // select which 2 values to remember, and where e, n.children[c] = angelicallyPermute(x, n, v, e) }

  23. ParasiticStack.pop pop(values) { // ask the angel which location we borrowed at time of push n = !!(e, values) c = !!(0 until n.children.length) // v is the value stored in the borrowed location v = n.children[c] // (1) select return value // (2) restore value in the borrowed location // (3) update the extra location e r, n.children[c], e = angelicallyPermute(n,v,e,values) return r }

  24. Let's refine the angelic program class ParasiticStack { var e : Node push(x, nodes) { n = !!(nodes) c = !!(0 until n.children.length) e, n.children[c] = angelicallyPermute(x,n,v,e) } pop(values) { n = !!(e, values) c = !!(0 until n.children.length) v = n.children[c] r, n.children[c],e = angelicallyPermute(n,v,e,values) return r } }

  25. First we observe what these angels do classParasiticStack { var e : Node push(x, nodes) { n = !!(nodes) c = !!(0 until n.children.length) e, n.children[c] = angelicallyPermute(x,n,v,e) } pop(values) { n = !!(e, values) c = !!(0 until n.children.length) v = n.children[c] r, n.children[c],e = angelicallyPermute(n,v,e,values) return r } }

  26. Refinement #1 class ParasiticStack { var e : Node push(x, nodes) { n = !!(nodes) c = !!(0 until n.children.length) e, n.children[c] =x, e } pop(values) { n =e c = !!(0 until n.children.length) v = n.children[c] r, n.children[c],e =e, values[0], v return r } }

  27. Refinement #1 class ParasiticStack { var e : Node push(x, nodes) { n = !!(nodes) c = !!(0 until n.children.length) e, n.children[c] = x, e } pop(values) { n = e c = !!(0 until n.children.length) v = n.children[c] r, n.children[c],e = e, values[0], v return r } }

  28. Refinement #2 classParasiticStack { var e : Node push(x, nodes) { n = nodes[0] c = !!(0 until n.children.length) e, n.children[c] = x, e } pop(values) { n = e c = !!(0 until n.children.length) v = n.children[c] r, n.children[c],e = e, values[0], v return r } }

  29. Refinement #2 classParasiticStack { var e : Node push(x, nodes) { n = nodes[0] c = !!(0 until n.children.length) e, n.children[c] = x, e } pop(values) { n = e c = !!(0 until n.children.length) v = n.children[c] r, n.children[c],e = e, values[0], v return r } }

  30. Refinement #2 classParasiticStack { var e : Node push(x, nodes) { invariant: c == n.idx n = nodes[0] c = !!(0 until n.children.length) e, n.children[c] = x, e } pop(values) { n = e c = !!(0 until n.children.length) v = n.children[c] r, n.children[c],e = e, values[0], v return r } }

  31. Final refinement class ParasiticStack { var e : Node push(x, nodes) { n = nodes[0] e, n.children[n.idx] = x, e } pop(values) { n = e v = n.children[n.idx] r, n.children[n.idx],e = e, values[0], v return r } }

  32. We have derived Deutsch-Schorr-Waite Marks reachable graph nodes • using constant space Uses pointer reversal • implicit backtracking structure • encoded by rewriting child pointers

  33. Summary Angels can stand for code that is as yet • unimplemented • not well understood

  34. Problem Sometimes too many safe traces Not all of them follow a plausible algorithm They abuse clairvoyance: one angel destroys a data structure “knowing” that another will fix it. Idea: summarize traces with angelic entanglement

  35. Entanglement-refinement relationship partition of angels all safe traces finer partition   refinement 1 refinement 2   refinement 3

  36. Bipartite Graph Classifier root.visited = true root.pol = 0 for (j <- 0 to numedges-1) { val e : Edge = !!(Edge) assert e.src.visited assert not e.traversed e.dest.pol = !!(0,1) e.dest.visited = true e.traversed = true assert  f . f.traversed  f.src.pol ≠ f.dest.pol } !!(Edge) !!(Edge) !!(Edge) !!(Edge) !!(Edge) !!(Edge) !!(Edge) !!(0,1) 1 !!(0,1) 1 !!(0,1) 1 !!(0,1) 0 !!(0,1) 0 !!(0,1) 0 !!(0,1) 1

  37. Hiding synthesis in tools

  38. Idea: Compilers based on synthesis Classical compiler: a rewriter based on • legality analysis • optimizing transformation Synthesis compiler: source program  partial program loc = E  if (??) loc = compile(E) else send(loc,compile(E))

  39. Summary Partial Programs amenable to inductive synthesis Interactive angelic synthesis frees from syntactic bind Synthesis-backed compilers may package synthesis

  40. Open problems Decision procedures, esp. for undecidable problems Modular synthesis, esp. top-down More interactive usage models, e.g. rapid prototyping More ways of encapsulating synthesis in tools

More Related