Counterexample-guided inductive synthesis (CEGIS [1]) is a major technique to deal with the quantifier alternation in synthesis problems. In general, a synthesis problem is to find a solution $f$ in some search space such that $f$ satisfies the specification $\phi$ for all inputs $x$. One can observe that such problems can be formulated by the following $\exists\forall$ quantifier alternation.
$\exists P.\forall x.\phi(P,x)$
However, solving general formulas with quantifier alternation is a difficult problem. Furthermore, even existing solvers for quantified boolean formulas with arbitrary quantifier alternation are not widely used in the synthesis community because they tend not to be very efficient for synthesis problems. CEGIS is a form of generating and check, where a synthesizer generates candidate programs that are checked by an off-the-shelf checking procedure. The key idea in CEGIS, however, is to use a checker capable of producing counterexample inputs. This allows us to use an inductive synthesis procedure instead of simply producing proposals blindly. As the figure illustrates, there are three components in a CEGIS solver:
┌----------------------┐ UNSAT ------→| Synthesizer |------→ UNREALIZABLE └----------------------┘ SAT| ↑ P*| ┌-----------------┐ | | Counterexamples | | └-----------------┘ | ↑c_i ↓ |SAT ┌----------------------┐ UNSAT | Verifier |------→ Solution P* └----------------------┘
Note that the synthesizer component is forced to produce candidates that work for all the counterexample inputs discovered so far, and hence only needs to solve problems without quantifier alternation, i.e., $\exists P.\wedge_i \phi(P, c_i)$.
In this assignment, you will implementZ3 is a powerful SMT solver, and Z3Py is the Python binding of Z3, which allows you to solve SMT queries in your Python program.
Some notable Z3Py methods are illustrated by the following program in which we want to verify an expression x-1
satisfies a specification
spec=f(x)>1
.
# construct the candidate expression x-1 x = Int('x') candidate = x - 1 # the synthesized function has type Int -> Int f = Function('f', IntSort(), IntSort()) # construct the invocation f(x) used in the specification invocation = f(x) # the specification f(x)>1 spec = invocation > 1 # we first substitute the invocation in the specification with the candidate expression # = x-1>1 query = substitute(spec, (invocation, candidate)) # we negate the query for the sake of finding counterexamples # = Not(x-1>1) query = Not(query) # check the query and print the result # >: sat s = Solver() s.add(query) print s.check() # get the model and print the counterexample # >: 2 m = s.model() print m[x]
More detail about Z3Py can be learned from the online Z3Py guide, advanced topics, and the Z3Py api.
The project uses Python2.7, Antlr4, and Z3Py.
Antlr4 and Z3Py can be installed with pip.
pip install z3-solver
pip install antlr4-python2-runtime
Then let's download the archive of the project source code. After decompressing it, you will find
parser/
: the parser of the simplified SyGuS problems;
testZ3.py
: the Z3Py example showed above;
ESolver.py
: an enumeration-based solver, which is already implemented but needs to be modified;
CEGIS.py
: the first partial-implemented CEGIS solver needs to be completed;
max2.sy
: an example problem of synthesizing a function computing the maximum of two integers;
poly.sy
: an example problem of synthesizing a function equivalent to 2x+2y
;
testZ3.py
and ESolver.py
with the following command to see if they
print the expected output.
python testZ3.py
python ESolver.py poly.sy
┌----------------------┐ ------→| Enumerator | └----------------------┘ P*| ↑ | | ↓ |SAT ┌----------------------┐ UNSAT | Verifier |------→ Solution P* └----------------------┘
ESolver.py
, which reads a given SyGuS problem file and search for the solution in the language of the following grammar G:
poly.sy
problem since 2x+2y
is in the language of G. However, we will fail to solve the max.sy
as there is no solution in the language of G.
So, our first task is to modify the ESolver.py
to enlarge the search space to the language of the following
grammar G':
In detail, ESolver.py
contains the following methods.
main(argv)
: the main method reads and parses a given SyGuS problem and call the enumerate
method to enumerate and check candidate expressions;
verify(candidate)
: the verification method checks if the candidate
satisfies the specification (global variable exprSpecification
), and, if not, return the counterexample as a model;
enumerate()
: the enumerator method enumerates expressions with growing size in L(G) and validates them by calling the verification method;
_enumerate_S(size)
: recursively enumerates expressions with size size
in L(S) and returns them as a list;
_enumerate_B(size)
: recursively enumerates expressions with size size
in L(B) and returns them as a list.
To enlarge the search space, you need to modify the method _enumerate_S(size)
and implement the method _enumerate_B(size)
(see TODO in ESolver.py
).
max2.sy
to see if it can find the solution if x>y then x else y
.
The approximated solving time is 1 minute.
python ESolver.py max2.sy
In this part of the assignment, we will work on CEGIS.py
. Different from the enumerative solver,
in CEGIS.py
, we maintain a list of counterexamples list_cex
(see line 55) in the enumerate()
method. Your first task in this problem
is to implement the update of list_cex
to accumulate counterexamples returned from the verification method.
With the help of the list of counterexamples, we want to reject candidates that failed on some counterexamples, before fully verifying them. You need to implement a
quickVerify(candidate,list_cex)
methods which check if the following formula is valid
$\wedge_{c_i\in list\_cex}\phi(candidate, c_i).$
If no, we can reject it and continue to enumerate the next expression. Otherwise, we verify if the candidate is a solution.
Hint: To evaluate the value of an expression containing no variables, you can use the simplify
method, which is more efficient, from Z3Py.
poly.sy
or max2.sy
(don't forget to paste your enumeration functions from Problem 1) and observe if the running time is significantly improved comparing to the enumerative solver.
On my machine, the running time of each problem is
poly max2 ESolver 0.82s 48.91s CEGIS 0.11s 11.63s
For this assignment, you should not modify the main method but are free to define new global variables or new methods.
To better understanding Z3Py, you may want to run and test the example codes in the Z3Py guide by yourself. And before writing code that builds SMT query, try to write the SMT query by hand.
Don't forget to test your work as you go along, rather than waiting until everything is finished!
Is your solver sound (i.e., does it always return correct solutions)? Justify your answer.
Is your solver complete (i.e., if there is a solution, will your solver return it)? Justify your answer.
ESolver.py
and CEGIS.py
which implements the enumerative solver and the CEGIS solvers, respectively.max2.sy
and poly.sy
.[1]Armando Solar-Lezama, Christopher Grant Jones, Rastislav BodÃk, Sketching concurrent data structures, 2008