Upload
katy-passey
View
221
Download
2
Embed Size (px)
Citation preview
ScalaZ3 Integrating SMT and Programming
Ali Sinan Köksal, Viktor Kuncak, Philippe SuterÉcole Polytechnique Fédérale de Lausanne
• Efficient SMT solver from Microsoft Research• Supports many theories through DPLL(T) and Nelson-
Oppen combination• SMT-LIB standard input format, as well as C, .NET, OCaml,
and Python bindings
• Blending of functional and object-oriented programming• Runs on the Java Virtual Machine (and .NET)
• Rich type system (generics, type classes, implicit conversions, etc.)
• Now used by over 100’000 developers (incl. Twitter, UBS, LinkedIn)
“Scalable programming language”
~$ ./demo
#include "z3.h"…Z3_config cfg = Z3_mk_config();Z3_set_param_value(cfg, "MODEL", "true");Z3_context z3 = Z3_mk_context(cfg);
Z3_sort intSort = Z3_mk_int_sort(z3);
Z3_func_decl f = Z3_mk_func_decl(z3, Z3_mk_string_symbol(z3,"f"), 1, &intSort, intSort);Z3_ast x = Z3_mk_const(z3, Z3_mk_string_symbol(z3,"x"), intSort);Z3_ast y = Z3_mk_const(z3, Z3_mk_string_symbol(z3,"y"), intSort);
Z3_ast ineq = Z3_mk_not(z3, Z3_mk_eq(z3, x, Z3_mk_app(z3, f, 1, &y)));
Z3_assert_cnstr(z3, ineq);
Z3_model m;if(Z3_check_and_get_model(z3, &m)) { printf("%s", Z3_model_to_string(z3, m));}
import z3.scala._…val cfg = new Z3Configcfg.setParamValue("MODEL", "true")val z3 = new Z3Context(cfg)
val intSort : Z3Sort = z3.mkIntSort
val f : Z3FuncDecl = z3.mkFuncDecl( z3.mkStringSymbol("f"), List(intSort), intSort)val x : Z3AST = z3.mkConst( z3.mkStringSymbol("x"), intSort)val y : Z3AST = z3.mkConst( z3.mkStringSymbol("y"), intSort)
val ineq : Z3AST = z3.mkNot( z3.mkEq(x, z3.mkApp(f, y)))
z3.assertCnstr(ineq)
z3.checkAndGetModel match { case (Some(true), m) ⇒ println(m) case _ ⇒ ;}
C and Scala side-by-side
def choose[A,B](p: (Val[A],Val[B])⇒Tree[BoolSort]) : (A,B)
def find[A,B](p: (Val[A],Val[B])⇒Tree[BoolSort]) : Option[(A,B)]
def findAll[A,B](p: (Val[A],Val[B])⇒Tree[BoolSort]) : Iterator[(A,B)]
Anatomy of an Inline Invocation
import z3.scala._import dsl._...choose((y: Val[Int⇒Int], x: Val[Int], y: Val[Int]) ⇒ !(x === f(y)))
Desired return types (actual Scala types).
Domain specific language of formulas resembles
Scala expressions.
Returned values are Scala values (including functions).
imports and Val[_]s only manifestations of
the library
for Comprehensions
for((x,y) ← findAll((x: Val[Int], y: Val[Int]) ⇒ x > 0 && y > x && x * 2 + y * 3 <= 40); if(isPrime(y)); z ← findAll((z: Val[Int]) ⇒ z * x === 3 * y * y)) yield (x, y, z)
Can you find positive x, y, z such that 2x + 3y ≤ 40, xz = 3y2, and y is prime?
(1,2,12), (1,3,27), (1,5,75), (1,7,147), (1,11,363), (3,11,121), (3,5,25), (3,7,49)
Returned expression.
Filter: arbitrary boolean expression.
Generators: sequences, computed eagerly or lazily.
Implementation Aspects
class Z3AST(ptr : Long)
abstract class Tree[+T >: Bottom <: Top] { ... def <(other : Tree[_ <: IntSort]) : Tree[BoolSort] = ... ...}
Bottom
Top
IntSort BoolSort … SetSort
implicit def ast2Tree(ast : Z3AST) : Tree[Bottom]implicit def tree2AST(tree : Tree[_]) : Z3AST
• Automatic conversions between the two kinds.• Soft typing for the DSL Trees.
Basic representation:
Tree hierarchy as part of the DSL:
import z3.scala._…val cfg = new Z3Configcfg.setParamValue("MODEL", "true")val z3 = new Z3Context(cfg)
val intSort : Z3Sort = z3.mkIntSort
val f : Z3FuncDecl = z3.mkFuncDecl( z3.mkStringSymbol("f"), List(intSort), intSort)val x : Z3AST = z3.mkConst( z3.mkStringSymbol("x"), intSort)val y : Z3AST = z3.mkConst( z3.mkStringSymbol("y"), intSort)
val ineq : Z3AST = z3.mkNot( z3.mkEq(x, z3.mkApp(f, y)))
DSL in Action
import z3.scala._…val z3 = new Z3Context("MODEL“ -> true)
val intSort = z3.mkIntSort
val f = z3.mkFuncDecl( "f", intSort, intSort)
val x = z3.mkConst("x", intSort)
val y = z3.mkConst("y", intSort)
val ineq : Z3AST = !(x === f(y))
Z3 Sorts
Int BV[32]
Bool
Array[A,B]
…
Scala Types
Int Boolean
Set[A]A⇒B
…
• Users should be able to ignore the underlying representation:
• Different function calls to create constants and to retrieve the models:
ctx.getBool(m.eval(tree))ctx.getNumeralInt(m.eval(tree))m.getArrayValue(tree)...
m.evalAs[Boolean](tree)m.evalAs[Int](tree)m.evalAs[Int⇒Int](tree)
def evalAs[T](tree : Z3AST) : T
trait Evaluator[T] { def eval(model : Z3Model, tree : Z3AST) : T}
implicit object IntEvaluator extends Evaluator[Int] { def eval(model : Z3Model, tree : Z3AST) : Int = ...}
m.evalAs[Int](t) m.evalAs[Int](t)(IntEvaluator)
implicit def lift2Set[T : Evaluator] = new Evaluator[Set[T]] { def eval(model : Z3Model, tree : Z3AST) : Set[T] = ...}
m.evalAs[Set[Set[Int]]](t)
m.evalAs[...](t)(lift2Set(lift2Set(IntEvaluator)))
def evalAs[T : Evaluator](tree : Z3AST) : T
def evalAs[T](tree : Z3AST)(implicit e : Evaluator[T]) : T
@implicitNotFound(“No known model evaluator for type ${T}.”)
Procedural Attachments
import stringTheory._
val s = z3.mkConst(“s”, stringTheory.sort)
z3.assertCnstr(s === “world” || s === “moon”)z3.assertCnstr(evenLength(s))z3.check
> Some(true)
z3.assertCnstr(substr(concat(“hello”, s), “low”))z3.check
> Some(false)
val z3 = new Z3Context(“MODEL” -> true)val stringTheory = new ProceduralAttachment[String](z3) { val concat = function((s1,s2) ⇒ s1 + s2) val substr = predicate((s1,s2) ⇒ s2.contains(s1)) val evenLength = predicate(_.length % 2 == 0)}
Applications
Z3 extension for sets with cardinality constraintsP. Suter, R. Steiger, V. Kuncak, VMCAI 2011
MUNCH: Decision procedure for multisets and setsR. Piskac, V. Kuncak, IJCAR 2010
Leon: Verifier for functional programsP. Suter, A.S. Köksal, V. Kuncak, SAS 2011, http://lara.epfl.ch/leon/
Other users at ETH Zürich, KU Leuven
Kaplan: Constraint programming in ScalaAli Sinan Köksal’s Master Thesis
https://github.com/psuter/ScalaZ3
Availability
http://lara.epfl.ch/w/ScalaZ3
(Tested on Windows and Linux, on 32 and 64 bit architectures.)
Thank you.