Transcript

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

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.

2

Partial Programs

originated in aLisp [Andre, Russell, et al, 2002]

if (choose) collect woodelse build barracks…switch (choose):North: go NorthSouth: go South

3

Synthesis from Partial Programs

Part. program P: defines a space of candidate programsA parameter c determines the candidate P[c]

Synthesizer picks a candidate that meets the spec: parameter c . inputs x . safe P[c](x)

Examples: P1: assert x << ?? == x*2

P2: for (i = ?? to linExpr(N,I,??)) …

4

5

Synthesizer: CEGIS

Inductive Synthesizer

buggy

candidate implementation

add a (bounded) counterexample input

SAT

UNSAT

fail

observation set E

okverifier/checker

Your verifier/checker goes here

compute candidate implementation from concrete inputs.

The CounterExample –Guided Inductive Synthesis algorithm:

Inductive synthesis step implemented with a SAT solver

6

Synthesizer: Convergence Testing

Inductive Synthesizer

buggy

Two candidate implementations c1, c2 + distinguishing input x c1(x)≠c2(x)

add an input-output pair (x,spec(x))

fail

observation set E

Evaluate spec on x compute candidate

implementation from concrete inputs.

[Jha, Gulwani, Seshia, Tiwari, ICSE 2010]

a correct candidate succeed

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

7

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]

8

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?9

Interactive synthesis

10

What's your memory of Red-Black Tree?

11

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; }

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

Jim Demmel's napkin

12

Programmers often think with examplesThey 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?

13

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?

11 22 33 33 22 11?

11 22 33

An angelic programOperator !! evaluates to a value yielding a safe trace.

reverse(list) {while (!!) {

!!(Node).next = !!(Node)}reversedList = !!(Node)assert reversedList is reversal of listreturn reversedList

}

Each trace of an angelic program is a demonstration.

a Boolean value

pointer to an existing object of type Node

correctness check

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)

16

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.

25

Depth-first search with explicit stackvroot = new Node(g.root)push(vroot); current = g.root

while (current != vroot) {if (!current.visited) current.visited = trueif (current has unvisited children) {

current.idx := index of first unvisited childchild = current.children[current.idx]push(current)current = child

} else { current = pop()

}

34

Node

children

idx

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 semanticsparasitic channel: for borrowing/returning storage

push(x,(node1,node2,…)) stack can (try to) borrow fields in nodei

pop(node1,node2,…) value nodei may be handy in returning storage

Parasitic stack expresses an optimization ideaBut can DSW be modularized this way? Angels will tell us.35

Replace regular stack with parasitic stack

vroot = new Node(root)push(null); current = vroot

while (current != vroot) {if (!current.visited) current.visited = trueif (current has unvisited children) {

current.idx := index of first unvisited childchild = current.children[current.idx]push(current, (current, child))current = child

} else { current = pop((current))

}

36

Node

children

idx

children

idx

children

idx

Angels perform deep global reasoningWhich 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

37

ParasiticStack.pushclass 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 restoredv = n.children[c]

// we are holding 4 values but have only 2 memory locations// select which 2 values to remember, and wheree, n.children[c] = angelicallyPermute(x, n, v, e)

}

38

ParasiticStack.poppop(values) {

// ask the angel which location we borrowed at time of pushn = !!(e, values) c = !!(0 until n.children.length)

// v is the value stored in the borrowed locationv = 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}

39

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

} }

40

First we observe what these angels do

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

} }

41

Refinement #1class ParasiticStack {

var e : Node push(x, nodes) {

n = !!(nodes) c = !!(0 until n.children.length)e, n.children[c] = x, e

} pop(values) {

n = ec = !!(0 until n.children.length)v = n.children[c]r, n.children[c],e = e, values[0], vreturn r

} }

42

Refinement #1class ParasiticStack {

var e : Node push(x, nodes) {

n = !!(nodes) c = !!(0 until n.children.length)e, n.children[c] = x, e

} pop(values) {

n = ec = !!(0 until n.children.length)v = n.children[c]r, n.children[c],e = e, values[0], vreturn r

} }

43

Refinement #2class ParasiticStack {

var e : Node push(x, nodes) {

n = nodes[0]c = !!(0 until n.children.length)e, n.children[c] = x, e

} pop(values) {

n = ec = !!(0 until n.children.length)v = n.children[c]r, n.children[c],e = e, values[0], vreturn r

} }

44

Refinement #2class ParasiticStack {

var e : Node push(x, nodes) {

n = nodes[0]c = !!(0 until n.children.length)e, n.children[c] = x, e

} pop(values) {

n = ec = !!(0 until n.children.length)v = n.children[c]r, n.children[c],e = e, values[0], vreturn r

} }

45

Refinement #2class ParasiticStack {

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 = ec = !!(0 until n.children.length)v = n.children[c]r, n.children[c],e = e, values[0], vreturn r

} }

46

Final refinementclass 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], vreturn r

} }

47

We have derived Deutsch-Schorr-Waite

Marks reachable graph nodes – using constant space

Uses pointer reversal – implicit backtracking structure– encoded by rewriting child

pointers

48

Summary

Angels can stand for code that is as yet

– unimplemented

– not well understood

49

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

50

Entanglement-refinement relationship

51

all safe tracesall safe traces

refinement 2refinement 2

refinement 1refinement 1

partition of angelspartition of angels

refinement 3refinement 3

finer partition

Bipartite Graph Classifier

root.visited = trueroot.pol = 0for (j <- 0 to numedges-1) {

val e : Edge = !!(Edge)assert e.src.visitedassert not e.traversede.dest.pol = !!(0,1)e.dest.visited = truee.traversed = trueassert 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

Hiding synthesis in tools

53

Idea: Compilers based on synthesisClassical 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))

54

Summary

Partial Programs amenable to inductive synthesis

Interactive angelic synthesis frees from syntactic bind

Synthesis-backed compilers may package synthesis

55

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 tools56


Recommended