Download presentation

Presentation is loading. Please wait.

1
**ScalaZ3 Integrating SMT and Programming**

Ali Sinan Köksal, Viktor Kuncak, Philippe Suter École Polytechnique Fédérale de Lausanne

2
**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 “Scalable programming language” 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) - Z3 just won most of the SMT-COMP divisions

3
**~$ ./demo I’m going to start by showing how it feels to use the system**

One part of Scala^Z3 mirrors the functions in the C API. We will come back to that later, for now I will focus on some of the things that we added as part of the integration.

4
**C and Scala side-by-side**

#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 Z3Config cfg.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 _ ⇒ ; } One thing this tool is: a mapping of the C functions into Scala. Almost one-to-one correspondence; uses object-oriented style and syntax standards differ

5
**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)]

6
**Anatomy of an Inline Invocation**

import z3.scala._ import dsl._ ... choose((y: Val[Int⇒Int], x: Val[Int], y: Val[Int]) ⇒ !(x === f(y))) imports and Val[_]s only manifestations of the library Desired return types (actual Scala types). Domain specific language of formulas resembles Scala expressions. Returned values are Scala values (including functions).

7
for Comprehensions Can you find positive x, y, z such that 2x + 3y ≤ 40, xz = 3y2, and y is prime? 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) Generators: sequences, computed eagerly or lazily. Filter: arbitrary boolean expression. Returned expression. (1,2,12), (1,3,27), (1,5,75), (1,7,147), (1,11,363), (3,11,121), (3,5,25), (3,7,49)

8
**Implementation Aspects**

9
**Automatic conversions between the two kinds. **

Bottom Top IntSort BoolSort … SetSort Basic representation: class Z3AST(ptr : Long) Tree hierarchy as part of the DSL: abstract class Tree[+T >: Bottom <: Top] { ... def <(other : Tree[_ <: IntSort]) : Tree[BoolSort] = ... } implicit def ast2Tree(ast : Z3AST) : Tree[Bottom] implicit def tree2AST(tree : Tree[_]) : Z3AST Two kinds of trees: the basic kind, and the typed kind. Conversions between the two is automatic. The typed kind of trees will catch some compile-time errors. Automatic conversions between the two kinds. Soft typing for the DSL Trees.

10
DSL in Action import z3.scala._ … val cfg = new Z3Config cfg.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))) 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)) One thing this tool is: a mapping of the C functions into Scala. Almost one-to-one correspondence; uses object-oriented style and syntax standards differ

11
Z3 Sorts Int BV[32] Bool Array[A,B] … Scala Types Int Boolean Set[A] A⇒B … Different function calls to create constants and to retrieve the models: Users should be able to ignore the underlying representation: 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)

12
**def evalAs[T : Evaluator](tree : Z3AST) : T **

def evalAs[T](tree : Z3AST) : T def evalAs[T](tree : Z3AST)(implicit e : Evaluator[T]) : T @implicitNotFound(“No known model evaluator for type ${T}.”) trait Evaluator[T] { def eval(model : Z3Model, tree : Z3AST) : T } implicit object IntEvaluator extends Evaluator[Int] { def eval(model : Z3Model, tree : Z3AST) : Int = ... } implicit def lift2Set[T : Evaluator] = new Evaluator[Set[T]] { def eval(model : Z3Model, tree : Z3AST) : Set[T] = ... } In fact, the signature is slightly more complicated. That : Evaluator reads as “evalAs is defined for any type T such that T has an Evaluator” It’s short for the long form with implicit So what is an evaluator? It’s something we define as part of the library: simply an interface parametrized by a type. The interface says “I know how to extract a value of type T from a Z3Model” Now what we do is that we define evaluators for the types we know how to handle. The implicit keywords mean that we don-t actually need to pass the argument: the Scala compiler fills them in by scanning for an implicit value of the right type. Hence, when we write, it automatically is expanded into … we injected type-dependent code, but the user doesn’t have to know! Now, I could define Evaluators for Ints, Bools, etc. What about type constructors? We can handle them too! Now, instead of one Evaluator, we provide a rule to build an Evaluator from another one. This reallys reads as Prolog: if T has an evaluator, then Set[T] has one too, given as follows. m.evalAs[Int](t) m.evalAs[Int](t)(IntEvaluator) m.evalAs[Set[Set[Int]]](t) m.evalAs[...](t)(lift2Set(lift2Set(IntEvaluator)))

13
**Procedural Attachments**

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) } 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”)) > Some(false) So far, we’ve seen how to communicate mostly from Scala to Z3.

14
**Applications MUNCH: Decision procedure for multisets and sets**

R. Piskac, V. Kuncak, IJCAR 2010 Z3 extension for sets with cardinality constraints P. Suter, R. Steiger, V. Kuncak, VMCAI 2011 Leon: Verifier for functional programs P. Suter, A.S. Köksal, V. Kuncak, SAS 2011, Kaplan: Constraint programming in Scala Ali Sinan Köksal’s Master Thesis Other users at ETH Zürich, KU Leuven

15
**(Tested on Windows and Linux, on 32 and 64 bit architectures.)**

Availability https://github.com/psuter/ScalaZ3 - Note that it’s probably also a good starting point for a Java wrapper. (Tested on Windows and Linux, on 32 and 64 bit architectures.)

16
Thank you.

Similar presentations

© 2017 SlidePlayer.com Inc.

All rights reserved.

Ads by Google