 Welcome everyone to the session the Z3 S&P Solver and Functional Programming by Alistair Bairi. We are glad that Alistair can join us today. Over to you, Alistair. Okay, hi guys. It's really good to be here. I presented on Functional Conference 2019 and that was a really good experience. I'm really glad to be back here. Well, just about me a little bit, my name is Alistair. I'm from Trinidad and we're a very multicultural country right now. It's Lent with the Catholics and it's also Eid, so we're actually having that together. People like Catholics and Muslims are fasting together, so it's a really cool, very multicultural country. So my presentation today is going to be on the Z3 S&P Solver and Functional Programming. So I just want to give a short background just on the basics of the symbolic logic and basically what is an S&P solver. So just our least symbolic logic is just a mathematical model of how we reason, how humans reason and where we consider it basically just manipulating symbols rather than the actual senses themselves. Okay, so usually the first definition is that each logic has a particular language. So for propositional logic you have true, false, P, Q, R, not, and then for first logic you have symbols like 4A. And of course, well you have different rules which define how the formula, how the formula can be well formed. So for instance, this formula, proposition, this is not a well formed formula because the implication operator is a binary operator. So this is the well formed. So basically, that's basically the overview of what symbolic logic is. You have basically formulae and you have rules that define how these formulas will be formed. So that's just really brief about propositional logic. I know you guys probably know all of this or just a brief overview. So we just have a proposition which is just a decorative sentence with a true thought. So it can be either true or false. Okay, so we know those propositions by P, Q, R, those symbols. And so we have P, Q, R, they are atomic formulae or atoms. And then you have the connectives. And we're all probably familiar with these. We have and, or, not, and twice. These are all logical connectives. And then computer programming languages like F sharp, we use symbols like or, or, and so something like this. So we would have basically this is an F sharp logical expression for proposition. So one proposition is that 6 is more than 5, which of course we know is true. And the other one is that 400 divided by 10 is less than 30. And of course when this is false. So these P and Q, these are our atomic formulae. And then the compound formulae would be things like not Q, P, O, Q, P and Q, P implies Q. And these things, they all, they all basically have a true value. Define that what the, whatever the operator is doing. Okay, so, so the main topic that we're going to consider today is satisfiability. So our interpretation is just an assignment of truth values to atomic formulae in a formula. And we've all seen true tables. And so basically a true table basically gives all the interpretations of a particular formula. And a model is just an interpretation where a formula is true. So let's see for this, so let's consider this compound formula, not P or not Q. And the, this interpretation here where it's true. So we have to set P to false, F to false to get this particular formula to be true. So this is these to that value, P is false, Q is false is a model this particular formula. Okay, and so when we have these atomic formulae, just we just have basically these symbols with P and Q. There are two n possible interpretations. So in this case, we have two atomic formulae P and Q. So we have four interpretations. Now, the thing is, if you have a very small number of atomic formulae, then it's very easy to figure out the interpretation. But this basically blows up really quickly with exponential complexity. So if you consider, for example, digital logic circuits, so this has just five inputs. And but this will give us, so this will give us three two interpretations. Of course, it just increases exponentially. So if you consider a circuit with 100 inputs, we're a way past, you know, something that you could feasibly be able to manipulate as a truth table. So the basic problem is that solvers like Z3 try to address is that Boolean, satisfiable to problem. So is there an integral interpretation of a propositional logic formula that satisfies it. So can we assign true or false to the atomic formula or the variables in a formula that would lead that formula to be true. Okay, and an equally important question is, is a formula unsatisfiable. Okay, so for instance, we know that P and not P that's unsatisfiable, that can never be true. Now, this the general satisfiable problem is decidable. You know, the very worst that you can do is just check the truth tables. Even if it will take you forever. That's the worst. It's decidable. And of course, SAT solvers today, they use a lot of really great algorithms to be able to reduce that time. And there are many, many SAT solvers today, many SAT, Pico SAT and Z3 also have SAT solvers. So, okay, so we looked at propositional logic. So the first thing, certainly when I look at now is first order logic. So first order logic basically goes beyond propositional logic. And first of all, you consider this particular set. That's the universal discourse. And these constants, these are the elements of the set. So it can be numbers, can be, you know, playing cards, anything you want. And the language of first order logic. You have quantifiers, you have variables. These variables, they are bound in the quantifiers. You have functions, you have predicates. This is an example of first order formula. For all X, Y, the predicate PX and the predate PYS implies X is equal to Y. That's just a simple first order formula. And of course, many mathematical theories can formalize using first order logic to them. So we can basically, so what is SMT? So SMT basically stands for satisfiability modulo theories. Now, we said that the propositional logic satisfiability problem is decidable. But that isn't true for first order satisfiability. That in general is not decidable. However, there are many theories or there are theory fragments that are decidable. So for instance, things like real numbers, integers, things like arrays, set detectors. We can actually form theories and take small parts of these theories. And we can actually create a decision procedure for those theories. And basically that's what an SMT solver does. That's what the model states when you say model mean within a particular theory. So satisfiability within a particular theory. So basically, so the problem is given a formula, is it satisfiable in this particular theory? And that's theory constraints how we interpret those symbols. So similar example is, this is an example of a formula that even though it's a first order formula because we're dealing with integer arithmetic, it is satisfiable in the theory of integers. And we can find that this formula is actually satisfiable with a is minus one and b is five. So that's basically what an SMT solver does. It tries to determine basically if it is that this formula can be satisfied and hopefully in a reasonable amount of time because we actually want to get the answers to those questions. So the Z3 solver, probably you all are probably familiar with the Z3 solver. Z3 solver, it's a solver from Microsoft Research. It's free and it's open source. And basically, when you have a particular formula, you can have many, many solvers in theories in a Z3 that you can use. Again, it's very popular theory. There are many, many cases. We'll look at some today in analysis optimization, verification, and things like checking your firewall rules if they're valid. There are many applications for SMT solvers and Z3 is one of the most popular ones. Okay, so just get into some of the details about how we go about interfacing with Z3. So the native language for Z3, it's a language called SMT lib2. That's sort of like a list language, but there are interfaces for many languages, c++, Python, .NET. Now, it depends basically the interfaces tend to depend on the typical language. What happens is that, for example, the Python interface is a relatively high level interface. But you can see here that the dot interface for language like C sharp, tends to be very low level and getting down into the low level details of using the solver. So what we want to consider this, how can we use Z3 in a functional programming language where we can write basically idiomatic functional language that is very high level and we don't have to do things like well, define custom types of approaches. We want to be able to reuse all of the built-in facilities of the language to be able to use Z3. Okay, so we're just going to start talking about F sharp quotations. Okay, so quotations are a very important part of F sharp. There are some lots of common quotations. Basically, what you do is you put code, your code, within these delimiters. And what happens is that the compiler interprets that code as a syntactic structure. In the sense of whatever expression you're defining. It also has a syntactic structure that you can manipulate and inspect. And really, quotations are really the primary vehicle for the metaprogramming in F sharp. So you basically have these syntax structures and use them for something else, something for your custom purposes. If you're writing a DSL, then basically this is what you can use to write a DSL in F sharp. And the cool thing about quotations is that the same compiler and the same IDP tier is like type checking, syntax checking, syntax highlighting. They are available for quotations. Okay, so let's look at a bit of a quotation. Okay, I have a simple F sharp expression. So it has a numeric value. Now if we put this in the quotation delimiters, what happens is that this gets into a symbolic expression. So now we have a symbolic expression. And you can see it's a very less, less sort of like expression. We have opposition and we have the parameters here. So basically this is a symbolic, this is a symbolic representation of that particular expression. And that this, and this particular expression will be able for us to use. So the expression has an integer and the type of the quotation, this is actually a quotation basically. It's a quotation that has that particular type system to it. And the types are, and you can also do things like if you define a function, then that particular function is available as well. So it's based on most, I can't really think of any syntactic like feature F sharp, but it's not available inside quotations. I think most of the features that you would get in F sharp, you can actually use them basically in quotations. Okay, so I just, just talking about the code a little bit. So the question, they have a particular type. So of course F sharp is a structurally type language. So all the, all the expressions are particular type and the type is EXPR. And so obviously there are two types. One is a generic and the other one is just a non-generic one. And what, so basically what else is you can actually use, you can actually use these expressions and these expressions are type check. So you can't actually compare say an expression of an integer with an expression of a real, of a real language. So that, that doesn't type check. And the most important facility is that all of these quotation types, they can actually match using pattern matching. So pattern matching is one of the most powerful features of course in functional languages. So we really would like to, we really would like to use that if you want to interface with our Z3 solver. Okay. So and so this is a quotation. So when you talk about a deep embedding, so a deep embedding is where you actually reuse the features and the structures of the language in your DSL, as opposed to a shallow DSL where you just have custom types and custom operators. So what we will be doing looking at today is basically how we can use quotations to create this deep embedding to use Z3. Okay. So just some more features of the, so we see this as the, so we have always saw the generality and this particular feature in the second cell, this is called, something that's called splicing. What does it do is basically, if you have one quotation, so we have J here, we can basically splice this quotation into here and basically get, and that's, so that's how we can manipulate quotations. Okay. And this, and here is, we're actually doing pattern matching. So here's one way, and there are many, many patterns. These two modules, these actually contain a lot of different patterns that you can use to, with your F-sharp quotations. So quotations are really, really powerful feature. They are one of the most powerful features of F-sharp. Okay. And the next thing, the last thing is that, so we said that all of the, all of the features that we would find in an ID or environment. So right now we're in an F-sharp kernel for Jupyter and we can see that basically if we try to add, so if this is a quotation, but we're trying to add an integer floating point number and that doesn't type, so basically it compiles as if, you know, this can't type. So all of the features that you would have when you're using ordinary F-sharp in your environment, they're also available in quotations. And so basically we have all those facilities. So that's one of the things that really makes quotations so powerful. We don't have to write any like a separate environment or separate compiler. The same facilities for F-sharp that we regularly use, they're available inside quotations. So here's where we have a function and the quotation, you have both the value and you also have the syntactic structure. So the value stays, you can still use the value. So in this case, we have a function that we can compute as function normally. But we also can also treat it as a quotation. I want to treat it as a quotation. We can actually get access to the actual structure of the function. So quotations are a really, really cool feature of F-sharp. And that's what we're going to be looking at a lot today. Okay, so let's talk about how we can go about integrating Z3 with F-sharp. These are some of the things we saw from quotations. Okay, so we want to be able to represent Z3. Well, of course, Z3 has its own language and Z3 has expressions. And these expressions, they have what are called sorts. Sorts are kind of like, they're not exactly, well, you can think of them basically as analog to types. Okay, and well, of course, when we're doing any sort of language work, we would want to use pattern-match. That's one of the most powerful features of functional programming on this, using pattern-match. It's going to simplify how we do code translation and how we do code generation. Okay, and well, you know, as I said, one way that we can, if you're doing like a shallow DSL, we can basically use, okay, just use custom types that are called Python also. Okay, so this is what, this is our Python. This is from the example of the Python Z3 library. So we have a custom type for an integer sort. We have a custom type function. We have a custom type for arrays. And basically, this is what we end up with. So this is an example of how Python interfaces with Z3. So what we would like to do it, so we would like to do what we saw with Python, but in a very idiomatic function always. So what we're going to do is we're going to use the XPR type that's built in from F sharp quotations. Okay, and by using that, we have a natural, it's a natural mapping. So, you know, we have number three, that's a primitive inside F sharp, and that is going to be our primitive inside for Z3 as well. And again, as you said, we have full support for these quotations. We have a really, really big library of patterns we can use, and it's very easy to compose your own patterns, whatever patterns that we need to compose them. Okay, so let's look at, so let's look at some of the things that we would like to do. We would like to be able to represent symbolic variables. So we saw in the Python example, we had an integer, we had a variable, it was an integer sort. Okay, so we want to be able to have, we have these different sorts. We have integers, real numbers, booleans. We want to be able to represent those variables. And of course, we have these all these, all of these operations defined in a first-order logic. We have these functions. We have these operators. And they also have these logical connectors. So we need to be able to define, we need to be able to represent these operations in our F sharp code. And of course, we would like to be able to represent things. So things like, okay, a system of equations, a sequence, you know, an optimization on how do you actually represent these things. If we have an objective function, we have constraints. How do you represent these things in F sharp? We want to wait as concise that that's very easy to use. And, you know, that also, that also gives us attest to the full power of Z3. Okay, so let's look at some F sharp code to see how we can actually go about translating these F sharp quotations. So how can we interface F sharp with Z3? So we just, so what we're going to do is, we're just going to, we just have one, really it's just one function that we choose. Really, we just have one function that creates a symbolic variable. Okay, so you can just peek at it. Okay, so this is our function. Okay, so we're just saying create a symbolic variable. And again, we're using a type, we're using types from the F sharp quotations library. So this is our type. This is a type that, a variable in F sharp quotations. So with this one function, we can basically just say, okay, if I have, so I can say that an integer, integer variable, a variable with integer sort, I just, it's a generic function. So I just pass in the type and similarly for a real value, I can just pass in the type as a real value. And those are the functions we're going to use to create all variables. And with the quotations, so if we have an integer variable and we have a real variable, we would like them, because they're different sorts, we would like them basically, we would like them to, not to type check. Okay, and this particular, like when we do this particular expression, but this doesn't actually, this doesn't actually type check. So I don't know if you guys can see the intelligence, but basically it says, okay, but this type, I can just run this. It doesn't work. Yeah, so basically our quotations are type check because so we cannot use two variables of different sorts and expression. So that's it. So this is a good preview. So like what we can do with quotations, using quotations interface with Z3 in comparison to Python, where Python does not have this idea, a native idea of static types. So static type check is one of the really, really great things that we can use.