Introduction to Program Synthesis

© Armando Solar-Lezama. 2018. All rights reserved.

Assignment 2

Submission Instructions This assignment is due at 5pm on the date indicated in the class calendar. The starter code for the assignment can be found here (only for problem 2). In order to submit your code, pack your Assignment2.js, default.html and question1.sk files into a tar file and submit through this form (same as last time). The form asks for the URL from where you can download your code. We will be downloading from all the URLs people submit shortly after 5pm on the due date.

Collaboration: You are allowed to collaborate with one other person for this assignment, but each of you is responsible for submitting your own solution. If you do collaborate with someone else, make sure to name your collaborator in your submission.

If you have any questions, please ask!

Problem 1 (35 pts)

The goal of this problem is to use Sketch to solve some non-trivial synthesis problem. Your goal for this problem is to synthesize a simple controller for an agent navigating a 2-Dimensional grid. The agent starts at position 0,0, and it's goal is to reach position with coordinates given by goal that are each between 0 and N inclusive. Scattered across the space, however, are some obstacles, so your goal is to write a controller that moves the agent from its starting point to its goal without ever landing in a position that includes an obstacle. Specifically, you must set up a harness that helps you synthesize a function with the following interface: void control([int n], ref int myi, ref int myj, int[2][n] obstacles, int[2] goal) The parameters are as follows:

The specification. The goal is to synthesize a control function that satisfies the following constraints.

Clarification: The control is not meant to reach the goal in one call. Rather, at each of the up to 24 steps that the agent is allowed to take, the agent calls the control to make a move and update its coordinates. After 24 moves, the agent should be at the goal state.

The program space. The space of programs is given by the following (very stylized) grammar: exp = 0 | 1 | -1 | rect(exp) | myi | myj | goal[??] | exp - exp | exp + exp prog = condassign* condassign = let tmpi = myi + exp, tmpj = myj + exp in if(!hasObstacle(tmpi, tmpj)){ myi = tmpi; myj = tmpj; return; } In other words, the controller is a sequence of conditional assignments, each of which tries a new update of the position and accepts the update if it succeeds in not hitting the obstacle. The function rect is listed below. int rect(int x){ if(x > 0){ return 1; } if(x < 0){ return -1; } return 0; } It is worth pointing out that in the grammar I am only using let as a shorthand notation. Sketch does not actually support a let statement, so those have to be actual variable declarations. In order for things to synthesize efficiently, your generators will have to bound the number of assignments and the depth of the expression trees. In order to solve this problem, you are likely to find the following flags useful: --bnd-inbits n bounds the number of bits for the input to the harness. --slv-lightverif Performs only lightweight verification. --slv-simiters n Performs n iterations of data-drive simplification before attempting to verify. In my implementation, the synthesis time was on the order of 10 minutes. Your solution should be written into a file named question1.sk.

Part 2 (65 pts)

For this part you will once again be working in JavaScript. The goal of the second problem will be to synthesize invariants necessary to prove that a piece of code is correct. We will be working in the context of a very simple language involving variables, simple arithmetic operations, and a set of simple commands (if, while, assignment and sequential composition). You will implement this in two parts, first, you will generate the verification condition as an AST using the same AST nodes provided to you for representing the program, and then you will generate sketch code from this AST.

a) VC as an AST: For this part, you may need to introduce additional AST nodes in order to represent the unknown invariants and to indicate the variables that may be used by this. Other than that, this should be a relatively straightforward implementation of verification condition generation.

b) VC as a sketch: For the second part, you will need to pretty-print your VC as a sketch so that we can rely on the sketch synthesizer to discover loop invariants for us. If you like, you can write your generators as a separate library that is included by your generated sketch, although you may do better by creating specialized generators based on what you observe in the program. The sketch should have a function named invariant_i for each loop in the program to help identify the loop invariants.

c) Test cases: You need to submit five test cases for which your system can generate interesting invariants.

Grading: In order to get full credit, your system will be tested against a relatively simple test suite of programs whose invariants will involve only conjunctions and disjunctions of linear equalities and inequalities. At the end, though, we will run all the submissions against everyone's submitted test cases and rank everyone's submission in terms of performance (how fast you can synthesize) and completeness (how many of other people's examples you can get), so you are encouraged to produce good generators that can handle more interesting programs.