 So, the did you get the virtual machine? Who has the virtual machine? So, the idea here is very simple. I will help you go through the internal states of the SMT solver such that you can understand how that three solver works probably, you know, CDCL. Okay, so what's happening is that, so we look at the inside the solver and so first we need some tools to, but it's already installed in your virtual machine. So, if you have the virtual machine, everything is installed there and we will see how to use it. So, once you want to use a solver, you want to understand, so working you need an IDE and a debugger. So, if we are going to use Eclipse, Eclipse is not the only one, actually I don't like Eclipse, I use Emacs, but I think I'm old and most people don't like Emacs. So, these slides I have changed to from last night from Emacs to Eclipse. You still have some references to Emacs. If you spot them, they're meant to be Eclipse, okay? Okay, so I hope, and I'm learning Eclipse since last night, so hopefully it will work, okay? So, yeah, so let's see. So, yeah, you can choose any IDE. So, the first thing is that you, so what, how do you do? So, you start Eclipse, okay? And when you start Eclipse, you will, let's suppose you press F11, okay? You should see this folder, that tri-build and you press F11, it will just basically restart the system. It pretends to compile, things is already compiled in your virtual machine and it, so it will start, basically start at the main entry point, okay? So, what happens is that, so the three is very complex software, it's a million, millions of lines of code. Once you do that, you will be completely lost. You'll say, okay, where should I start looking? How do I understand this big code base? And once you have this kind of experience, it overwhelms you and if you want to develop in this kind of field, you should be able to comfortable with that feeling, I don't understand what's going on. And I have a sort of understanding, what will go to them is being implemented. And how does it connect to actual functions and classes, I have no idea. But I try to look bits and pieces of it, start building a picture, and I will never have a complete picture. And based on that sense of judgment, I will make my changes, whatever, I have a new idea, I want to implement, I want to change the restart algorithm or whatever part of it you want to change, you change it. And hope your hunch is right. And that's the way to operate. And if you hope to get the full understanding, you'll spend months and months reading the code, it will never finish or you'll never have an understanding. So let me recap the, what is the, we're going to see today. So first thing with term management in Z3, the, what is the term management is when you have such a solver like something like Z3, it has to handle a lot of formulas. We usually talk about some complexity of solving the formulas. But actually building the formulas and solving itself becomes very complex business once you are handling quantifiers and substituting a formula, formulas. For example, let's suppose you have, let's suppose you construct a formula x plus y greater than or equal to 3. And you, some point of time you substituted x by z. You created another formula z plus y greater than or equal to 3. So what happens is now there are two formulas in the store. And if you are, let's suppose, building formula, substituting m. And again, I once, nine users keep doing them. And they never throw away old references. So it gets clogged up in your memory very quickly. So Z3 has to be very smart about it, how it manages the terms you create, and how it intelligently gets rid of them when they are not of use. And secondly, when it has this term store, it's a, so it has to separate from the solving and the term stores. So when you create a, when you start a Z3, it has a context object. Context object has a term store in it. So every, you can create multiple context objects. Every context objects have different term stores. And every context can create many solvers. So these solvers share the same term store. So that's the, that's the idea you need to understand. And what happens is once you're building a tool, people end up managing their own term stores, okay? So you should always use Z3's own term store. You are interacting with Z3 because what happens is Z3 is fairly clever about it and that's very sophisticated component. And if you don't use it and you try to maintain terms yourself, what you will keep referring to, you need to keep a map Z3 term store to your term store. And because you always have this sort of reference active, Z3 cannot throw anything away. And because of that, you will just fill up the memory, okay? So you have to be very mindful how you use them, okay? I will come to what is the tactics layer. Then there's CDCL implementation, okay? CDCL implementation, the way you see it, it's very close to it. The way you see the algorithm, you'll see the source code. I will show you today. It implements very, very close to the algorithm. But there are a lot of clever optimization there. I will go to one of them, but there are hundreds and dozens, hundreds of them. And if you don't understand them, I take a look at them, okay? You will learn a lot about how to program well. And those tricks and techniques as earlier speakers have said are not covered in any textbook. So today I will just go on an example of theory of equality, okay? So what was the CDCL algorithm? Let me recap. So what happens is you have a CDCL, a CDCLT, okay? So in a CDCLT, you have this basically Boolean solver. It has a Boolean problem, a world. And what it assigns bits to true and false. And these bits are corresponds to theory terms, okay? And then it sends to theory solvers. And sometimes it says, okay, unassigned something. It's a backtracking, it sends, call is sent. And time to time, it not deterministically says, okay, what is the current state? Is it checked set or not? Okay? Is it satisfiable or not? And if in response, at this point it may give a response that what other things, because we have said these things true, these other things may also have become true. Or it can also respond that, oh, things you have said so far are contradicting each other and here is the reason of conflict, okay? So this basically covers the overall architecture, okay? So we are going to look at this problem, okay? So this is a smallest example I could construct. And where it actually goes to solving. Anything smaller I constructed, the pre-processing step solves it. Basically what happens is there are a lot of patterns in Z3 and it keep replacing formula with using those patterns and very quickly discovers false or true, whatever it is. And so this is the smallest thing I could construct. So this is still in such a short setting, difficult to follow, but let me see what, let me show you what's going on. So this is about function F, which takes, U is an interpreted type and function, so these are the facts we know about function, okay? F, so F, F square, let's say F of F. F square is A equals to A or F to the power four, I think. A equals to A, okay? So second statement says F cube equals to A or F to the power square A equals to F of A, okay? So last is saying F of A is not equal to F of A. Okay? Is it satisfiable or unsatisfiable? How would you know it and how would you, how the solver may do it? So I mean, I decide you make a guess, okay? Let's suppose, yeah, you were saying? I said one would do by substitution now, F of F to A equals, so. So let's, but there's a distinction here, right? So first make a choice, which one is, which of them is true? Yeah. So let's suppose solver decides this to be true and this to be true for both the clauses. So, so what happens, there's a method called union find. So that's the implement union find to find these, to solve this constraint. What happens if you have basically five terms, there's a one term here, F of A is another term, F square is another term, okay? Term one, term two, term three, term four, term five, okay? So what will it do? It maintains the equivalence class of them, okay? So it create, initially it create five different equivalence classes, okay? And it knows that F of A is not equal to A. So it is one and two. Yeah, yeah, yeah. So basically, yeah, so what happens is, in case it cannot be made equal, okay? So as soon as you put something equal, suppose you say F square is equal to A, so it, what it says is that, which is three equals to one, okay? So it may say, okay, they are equal. If one and three are equal, if you apply F, because that's a property of function, okay? If they have the same parameter, the output is same, okay? So if these two are equal, if you apply the function F on them, they also should become equal. Where is that, like the fact that it's a function? By declaration. That is a function. Yeah, now Z3 will interpret it as a function. And that is the axiom that will be used to compute it, okay? So what will happen is, if you apply the function F on both the terms, it will get these two terms. They become equal, okay? Okay? Now on these two, if you apply a function F again, you get this guy can become equal, okay? And do you see contradiction being derived here? No. You need F cube A also equals to A, right? So if F cube A equals to A, so since F cube is equal to A, we already have this edge, okay? So this edge is not important. So once these two edges are created, you have a, there's a path here and there's no path there, okay? So that's how it works, okay? So now we are going to see that how Z3 does this kind of reasoning, okay? So that's, we saw the example, okay? So, okay, so since we have launched the solver last time, okay? Let's go ahead, let's get to, let's run it, okay? So if you press F8, it will finish it and give you a sat, okay? Let's see. So yes, okay? So what happens is, so what we want is to put a breakpoint somewhere to such that program goes somewhere and stops, okay? So what I have done, I have put a breakpoint somewhere and that's what you need to do. You need to find this file called SMT underscore context.cpp which is at this location, okay? So if you go in the build, Z3 build folder, you will find the source directory node and then you go inside, you will find this file and you put a breakpoint in there, okay? Line number 3366, okay? If you stop there, okay, then you can do the interesting thing. That's where it becomes interesting, okay? Okay, so, so what you can do, here you can see, you can probe the internal state of the solver. There is something called display. So what is a display C error? What is, what it does? It basically prints the current state of the solver, okay? So this is the current state of the solver and what you can do is this, that first you see the term store. The term store says assigns every term in number, okay? And it includes true and false also. So truth, so binary values are plus class, citizen and Z3, so they are not differently treated, okay? And you can see that it also listing the list of formulas which are, which are basically asserted. Formula number 30 and 33 and 35 are asserted in the system. And it also tells you something more, which is not important for now, okay? Yeah, it is good. So here is a state, okay? It has, it is telling me there are three formulas which are asserted, which are these three formulas, okay? And it is said that I will come to it what these, these things are and it's showing you the current state of the term store, okay? So these are the, it's a starting point, okay? Then what happens is you, you run the solver, okay? And get to the, there's a step called internalized assertions, okay? This, this step, okay? At this step, it goes over the formula and initializes all the relevant theories it has to handle, okay? So for example, in this particular formula, there is no integers, there are no integers, no rational numbers, no talk about arrays, so it won't allocate memory for them and only allocate memory for the, for equality reasoning, okay? Because it sees the functions being applied and they are being equated, okay? So, so let me put a break point here and execute to, okay? So after this, I would like to see the current state. What has happened because of, so I display, so if I want to see the current state, now what has happened is, okay? So what has happened, what has happened is it, it goes over all the terms, okay? And it figures out that every term, corresponding to the every term occurring in the formula, it creates a node like this, okay? And it has not, it has not processed the formulas yet and created a, this kind of edges yet, but it has created the nodes. Note that it is not only creating nodes for the terms which we think are relevant, but also creating nodes for the atoms also, for example, all the equality symbols, okay? So if you say a equals to b, that also is a term within this equality store. So what can happen, it can, it's very clever sometimes. So sometimes it can discover that, that you are making a not equals to b, both are Boolean types and b is not equal to c and c is not equal to a. And if they are all Boolean types, then it's wrong reasoning, okay? So it can discover by equality reason, that by doing the equality reasoning, okay? So that's why you see so many terms being created for the, just for equality reasoning. So no matter what, so whatever theories you are have activated, all of their internal states you start seeing there, okay? So let's move forward and then what we see is after this it continues and we'll see continue, continue and this step, this is called search, okay? So we need to step inside, I have search. In this search function is actually the solving happens, actually this is not the function. Let me go with the continue, we need search, this is not the function, that is the function. So let me get inside it by pressing F5. This is what is this, what is this? This is, what is this? Some students should say what is this, propagate, what is being propagated? So what you'll do, it's a Boolean PCP, yeah, it's propagates the, it's a unit propagation, okay? Not only unit propagation, it also does the theory propagation. If their theories are involved, for example, equality reasoning here, if whatever theory has to be propagated, it will propagate there and get back all the implied facts from the solver, okay? So, okay, so let's see if I run this, okay? So you get another internal state and now you can see that, oh, this formula store is not that important, I can just, okay? So now you can see that it has, because there was a unit, look at the current assignment, okay? So the current assignment is, since you have a unit clause, all the unit clauses are set to true without thinking, okay? So when you did the propagation, they are set to true, okay? And it also telling you in which order it is going to set the other literals true or false, okay? So remaining case splits, it's still telling you, it is going to set, there are four more atoms, okay? So they are 1, 2, 3, 4, the four more atoms in the system and it has to set them true or false in some order, okay? So it has already defined the order it is going to set them true and you can see that there's nothing, no, nothing has changed the set of equivalence classes, okay? And soon it will change, okay? So let's continue, okay? Now what is this step? Decide, what happens? Somebody say, an out loud student, what is decide? Yes, it will, it will, because there are four literals yet to be decided and not all of, all the asserted clauses have become true. This is true night now. We don't know the value of these two guys, right? So we, to make them true, I need to set some of them true, okay? So what will I decide? It picked the one of those literals and set them true or false, okay? So let's see if we go over this step, what will happen, okay? So if we go this step, so we can get another dump and, okay? So now you see that 26 is said to be false, okay? So one of the atoms has been said to false. However, that atom never occurred positively in the, if I were implementing the solver, I would just set it to true, okay? Because it will make the clauses more likely to be true, okay? But that's not exactly entirely good idea always. Why they may, that may be the case? Because once you set it to the true, you're reducing the work of the Boolean guy, Boolean solver, but you're increasing the work for the theory, okay? So it's never clear. Setting a literal true or false is going to reduce your work. So if the word of a self is always not clear. So I think Z3 always set it to false initially, okay? Actually, I'm not sure what happens in general, but in this particular code, this particular version of it always does that, okay? However, it's changeable, okay? It's modifiable, okay? So once you set this to false, then what happens? What should happen, okay? It has set one of them to false, okay? So it has set f square equals to a to false. So this is false, then what will happen if I do the propagate next time? It has to make this guy is true, okay? So and once you make this guy true, what will happen? This algorithm will trigger, okay? So what will happen is that as soon as you set f1 equal to f4 equal, then this algorithm will trigger and start building equivalence classes. So we'll see that what happens next. So we go with Sykes again. So does the propagate, okay? And let's see the state, okay? So once you did this, okay? So you see that once I said this was unit clause, now I said this one to false, it forced, yeah, basically it's this guy, okay? So it's set 29 to be false, to be true, okay? Now because of this equality, you see this thing happening here. It is said that f to the power 4a equals to a, okay? For some reason, it didn't say, okay? This was been set to false, therefore this has become true, okay? Therefore it has put them into the same equivalence class, okay? So you can see that it is now start showing you the equivalence classes, okay? This guy is equivalent to this guy, okay? Now this is true, now this is false, okay? Maybe, now still we have not decided. So what will happen? It will, it will has to decide one more time, okay? So that's f6, f7. If you continue, it comes back to the side again, right? So if once it decides again, what do you see, okay? So now it, what is the happening? The current assignment, okay? It actually after decide, it needs a sort of propagate also. Then only you see the effect of decide, okay? So it has made another decision. It said, again the, it picked, I probably picked one of these guys and set them to false, okay? And so that is happening, one more thing has become false, okay? Now the propagation has to happen, okay? So let's get to the propagate, yes, okay? If we execute this, now it created all the equivalence classes, okay? Okay? Now all these literals has been, since this was false, this forced this guy to be true because they are a part of the same clause, okay? So now this, since 29 and 23, as is 29 and 32 became true, it triggered this whole chain reaction of equivalences, okay? And it made all these terms equal, okay? One thing to note is this, A is equals to F square, F of A is equal to F square A, F cube is equal to F square A, F to the power 4 is equal to F square A. If you know union find, you can see why that is happening. There is always a master term, okay? And everybody has a pointer pointing to them, okay? And by this way, you can very easily manage the equivalence classes. And so since you have a backtracking, you need a very clever way of what you need to do is, clever way of merging the equivalence classes and splitting them, okay? So what is being done is in this way, what happens is the circular linked lists are maintained, okay? So each equivalence class is circular linked list, okay? So if you want to join a circular linked list, they have a, they both have a master node. And what do you do? You switch the cross link, their pointers, and then becomes a bigger equivalence class. When you have to backtrack, it's very simple. You just go back to do this unit operation. Very efficient way of implementing equivalence classes. This is not the standard implementation you see in the textbooks. Textbooks, they will tell you you find the rated recycling graphs and you merge these graphs. And each time you need to know for a node, who is my master, you traverse this graph up and you get to the root and then you pick both the roots and then merge them, okay? So that is not how it is implemented in Z3. The primary reasons, they do not implement in a very standard way because of backtrack requirement, okay? So now let's continue. What should happen now if I continue? Conflict, okay? Because you have made things equal and you somebody is saying they are not equal, okay? And then backtracking should trigger, okay? So if you continue some point of time, it has to do the conflict, dissolve conflict. It has already resolved and it will backtrack. Let's not get into too much. Let's see after propagate what's the state, okay? So anybody can guess what's the state should be right now, okay? Okay, still has not realized it, okay? So I probably don't fully understand how it works. So let's do one more iteration, okay? Okay, let's see what's the current state, okay? So it's still everybody in the same equivalence class. Let me get again. So what it has to do is when it is, this is a conflict has to lift up to the Boolean world, okay? So it has seen that, so what it has to derive fA equals to A as an atom and pushed it into the Boolean world and they create A not A in the Boolean world and that what is it doing in so many steps, okay? Okay, yes, now it's going to trigger, okay? So what it has found is this. Once it, that conflict was detected, it realized the decision it made to set this one to false and we're forcing this one true, create the conflict, okay? So in result, what has happened, it's realized, it learned this conflict clause, this has to be true, okay? Now this is made true. Once you made this true, everything will basically fall into the place and it will just immediately realize that everything is sunset, okay? So yeah, that's what immediately happened and immediately realize everything is sunset. So this is very short talk I wanted to give to just show you that how you manage, how you can look into the big solver inside it. One more point I want to make is look at the size of the call stack, okay? So this is, you are within the solver and there's a very long call stack, okay? Anybody knows why that is the case? Before solving, reaching the solver, one thing Z3 does and then starts to solve it and why there's so many similar looking calls? What is going on there? Any guess? So there is a tactics layer, okay? The idea is that Z3 is not a one solver, it has many solvers inside it. So I showed you one particular file, there's a separate set solver in it, okay? This is assembly solver component. If you just give the only Boolean problem, the execution goes completely different place, okay? So what happens is when you give it a problem, it guesses what is the best solver it should use, okay? It tries that. If it fails, it tries the another solver for a while. So what is the policy of managing these solvers, okay? So the portfolio management of this portfolio is this called tactics layer, okay? And some of them are purely syntactic analyzers. They just look at the formulas and tactically try to guess that how it is unsatisfiable. And often the problem just go there and don't go past it, okay? So this is the pretty much what I wanted to show you. And I would encourage you to just take this VM and in future, if you try to look around it, and in future if you want to add any feature, try to look inside the Z3. Sometimes very, very functions you want to implement are already been tried inside Z3 and abandoned, okay? So you would have some idea that you should not close the idea they have already tried. And lot of code inside Z3 is just like this, okay? So I have tried it. For example, I was very much interested in the theory of partial orders, okay? So I implemented my own partial order and I was very excited it's working. It's better than not there. And then one day I showed it to Nikolai. Oh, yeah, yeah, yeah. We built this thing. And then, oh, look at this version number 3328 and it is there. And yeah, I checked it, checked out and it was there, okay? And it was much better than what I had built, okay? So it is important. You just don't go on without looking, okay? Please pay attention to the source code. And they are very well engineered tools. And if you understand the source code, you will have improved your programming skills. Thank you.