 Can we start? Hello, testing. Unfortunately, I can't see the time, but I think it's 11 o'clock. So we'll start. So welcome, and thanks for coming into this session on optimizations using Mini-Zinc. Quick one slide about myself. My name is Melvin. By day, I work at the Intelligent Warehouse to do warehouse automation solutions by night. And on the weekends, I work on Mage Arena, which is an open source program using good old fashioned AI, actually, to attack the problem of the card game magic to gathering. I gave a talk on this at Boss Asia last year. You can find the video online. But today, I'll talk about something else that's more related to the work I do with warehouses. So let's look at some optimization problems, right? So it's actually quite common. So if you use Google Maps to find the path to get to the Lifelong Learning Institute, you probably use the fastest path, the shortest time that would take me to travel from where you are to this place. This is a kind of optimization problem, finding the shortest or fastest path. Another kind that you definitely did it yourself back in school was when you did some experiments and you wanted to find the best fit line, which tells you the theory of this particular phenomenon. And the line has to be sort of close to all the experimental points. So this is also some kind of optimization because the lines have to be as close as possible to all the points. Of course, it's not possible because the points are spread out. And much more recently, if you do machine learning type work, machine learning, the training phase is essentially also an optimization problem because you want to find the set of parameters that would reduce the error of your model. And if you think a little bit harder, actually you've done this before because finding that best fit line in the last slide is also some kind of machine learning. And this line is also some kind of model, just a very simple model. It has only two parameters, which is the slope and the intercept. Of course, modern day models have thousands of parameters, but the concept essentially is the same. Just it's scaling up in terms of the number of parameters. Of course, we can no longer do this by hand if we do this with an algorithm. And most optimization problems you encounter in the real world are more like the latter ones. They are fairly complicated. And there are no fast ways to solve them, none that we know of anyway. This is sort of an XKCD comic that describes this problem. Usually we have many different real world constraints. And adding all these constraints makes the problem hard from a computational perspective. And so this comes to this topic, which is if we have all these hard problems with many, many constraints and there are no efficient ways to solve them, what can we do? We still want to get some good solutions because these are practical problems. And there is this whole subfield of computer science called constraint programming. And that is devoted to this study of how do we solve all these problems? Essentially, we solve them with search. Just like you've seen in the previous talk, if you were here, we search through a tree of possible options and find the best solution. And there are some of the tools that you might have heard about or have not. These are called constraint programming solvers. Essentially, they perform a kind of search, but in a somewhat clever way to avoid enumerating all possible solutions. It just tries to search in the set of possible or valid solutions. Most of these are open-source projects, I think, except for C-Plex and GroBi. These are commercial solvers. So the problem with ultimately solvers in the past was each of these solvers require for you to describe the problem in its own custom language. So since this is search, the search is already implemented for you. So what we need to do is just to tell the search or the solver what is our problem, to model the problem and let the solver do it for us. But each of these has different input languages, so there was kind of trouble. You've got to decide in advance which solver you want to use. And here comes the gist of the talk, which is about this particular modeling language, which is called MiniZinc. MiniZinc is a solver-agnostic modeling language. So it doesn't dictate what solver you need to use. It actually supports all of the solvers on the previous slide. So here comes the difficult part. I'm going to do a number of demos. So the first demo is something that you might find in the Singapore Mathematics syllabus in primary school. This is called a word problem. So I thought I will have hands to type, but I guess not. So I was going to type out the problem. So here is the MiniZinc IDE. You can also run it in the command line, but for the purpose of demo, it's easier for me to use the IDE. Since I only have one hand, so I will not try to type this out. I have typed it out beforehand, just in case. So how it works is we're going to describe this model to be using the MiniZinc language. This is a modeling language. Note that this is not a programming language. This is a modeling language. So there are a few parts of this language. First, we have to define what are our unknowns. Here we call it the var or the variables. This is variables in the mathematical sense, not variables in your programming language, because they're essentially fixed, right there. But they're unknown. So we have the ages. So the problem is replicated on top if you forgot. It's a very simple problem, actually. But just to illustrate how you would model this very simple problem in MiniZinc. So we declare the unknowns. This is something a bit like Scala syntax, maybe, like type colon, and then the name of that variable, and then semicolon to finish off that declaration. And then we have the line about the sum. The sum is actually what we call a parameter. So it's called par. But you can leave out the word par. So just type int colon. So the sum is in 41. This is the total of the ages. And then we have three constraints, because there are like three sentences. Jake's age is three years older than Kyler. Jake is two years younger than Larry. Actually, if you think about the letters, it's actually j, k, and l. But anyway. And the sum of the ages is this constant, 41. And then the last part is just to say, what do we want to solve? In this case, we just want to solve for some answer that satisfies the above constraints. And you can see the key here, really, is the constraints. And this is why this is called constraint programming, because we didn't specify the constraints. And then run the model through the solver, which I preconfigured. And you can see that it generates the answer. I mean, of course, in this case, you could solve it by hand. But the point is to just illustrate how it works. So Jake's age is supposed to be 14, which is the correct answer if you solve it by hand. OK. So let's get back to the talk. So how does this actually work? So I said we don't have to choose a solver. But of course, at the end, we have to eventually pick one to use to solve the model. Minizink is both a language, which you use to describe the model, as well as a compiler. So the Minizink compiler actually produces a file in a format called FlatZinc. So this FlatZinc format is actually simpler than the Minizink language. It has much less features. And the solvers you see listed here have built-in support for FlatZinc. So each of these solvers can take in a FlatZinc file and solve for the unknowns. So that's what happens. When you click Run, the compiler runs, it produces the FlatZinc file, and then passes it to one of the solvers to solve for the answer. So you only have to choose a solver at the last step. And you can just change solvers quite easily by changing in the configuration file which solver you would like to solve your particular problem. Because some of these are more specialized to work certain types of problems. And I can do those much faster. So the rest of the talk, we'll look at more examples of modeling and Minizink. So come back to my example at the very beginning, this XKCD comic. Excuse me, I haven't actually seen this comic before. Oh, quite a few people. OK, that's good. But anyway, if you haven't, let me quickly tell you what it is about. It's like more complicated than the previous problem. So you have a menu with different appetizers. And the character in the comic says, we like exactly $15.05 worth of appetizers. The keyword is exactly. So you have to have some combination of maybe three of some items, two of some items, and so on, so that the total cost is exactly $15.05. So this is what we call NP-complete problems. It's also called the knapsack problem. So how do we solve this in Minizink? Let me get back to my ID. So let's see. Again, I think my screen is a bit cropped up. But let me just crop away the output window for now. Because I didn't realize the resolution would be like this. OK, so we'll just say, OK, how many items do we have? We have six possible appetizers. And Minizink is actually somewhat high level language. So model language actually allows you to use things like arrays, so unlike other languages. So we can define an array. The indices are some integers. So in this case, we'll say the integers from 1 to n. You can use arbitrary sequence of integers for arrays. So we say like this is an array of int. That's the type. And this particular parameter, and this is a parameter because these are all known values. It's called price. And the price is this. This is the same as the prices of the appetizers on the menu. So I'm just rounding them. I just use integers because I don't want to deal with floating point inaccuracies. This is 1505, which is the total value of the, there you want to spend to buy appetizers. X is the unknown, as in solve for X. X means, in this case, it's a complicated structure. X is actually an array of var int. So it's array of unknowns. So what does it mean? X means how many of item 1 do I buy? How many of item 2 do I buy, and so on? That's the value in the array. So the index represents the item, and the value of the array at that position is the number of such items I would buy. Well, never mind. Forget about Z for now. So constraints. OK, now we need to write slightly more complicated constraints, but still fairly readable, I think. So we want to have something about the total. We're going to say the total is equal to the total price, which is the number of items, the IF item, times the price of the IF item sum over all i from 1 to n. If you're familiar with mathematical notation, this is the giant sum, and ranges over i from 1 to n. And this sum must be equal to total, which is 1505. That's the total cost of buying all these appetizers. And just to avoid certain impossible cases, this is because xi, we know x represents how many items, so it can't be negative. And this additional constraint is just to say that all the xi's are positive, well, are non-negative integers. It can be 0, of course. 0 means we don't buy any of that item. And it's just somewhat slightly fancy thing to show the output. Output is accepting a list of strings. The show built-in function converts the integer to a string and plus classes string concatenation. So we just output the, so let me just show you the result. Here we go. So it turns out you can just buy seven pieces of the first item, which is the mixed fruit, which it works, but it's kind of weird to just eat mixed fruits, because you have so many different kinds of appetizers. So it's not a problem, actually, because in fact, there are more than one possible answer. So here's where we're going to do the optimization part. So we'll say, OK, so now we declare another var int xi to be the total number of items I'm buying. So in this case, z will be seven. See, z is how many things I'm buying. So we'll say, OK, let's not buy so many things. So we want to find a solution that minimise z. You would say it won't satisfy you, and there's actually many solutions that actually give you all of them? Yeah, it will give you all of them. But in this case, I've configured it to only print just to save space. But you could say, find me all the solutions, actually. But in this case, I just want to show this possibility that instead of saying satisfy, we can also say either minimise or maximise something, which is like finding the shortest path, finding the model, the least error, that's minimised. So we want to minimise the number of items you want to. Maybe you can't eat so many things. So you don't want to eat so many things. So we're going to say minimise z. And we save it and run again. We get a different solution, which also adds up to 1505, I think, which has a variety of items anyway. So that's kind of nice. Of course, we could declare a more complicated function that says we want to reward a solution that has a variety of things, whatever that means, and maximise that particular score. That's also possible as well. Or we could even say something like, well, I don't know how much. So how much time do I have? How much time do I have? How much time do I have? Sorry, just checking the time, because I can't see the clock. Sorry? 17 plus. Ah, OK, 17. All right, I think I have time. So set this. I see the next figure is 11.40. All right. So we can also say constrain constrain constrain x1 not equal to 7. Like if we really want, let's try. Yeah. So if we don't want to eat so many of the first item, we can just say we don't want so many. I mean, that sort of shows the power of constraint programming, right? We just have to write a new constrain. We don't have to write any code. I mean, it's not really code. But this is code, but this is like describing what we want to do. I was telling someone about this talk. It's like writing a query if you understand database. We just say, find me this, where this, this, this, and so on. I don't really know how he's going to do it, but I don't bother about that. So it's very similar here, except this is much more powerful. It lets us solve all kinds of hard problems. OK, so let's move on to the last. So I don't want to overstay my welcome. Let's move on to the last. That's the answer. So the last one, I guess, I wouldn't solve it for you. I will show you how to model it. And maybe you can go back and try and solve it and see what's the answer. I guarantee you there's an answer, but I'll show you how you model it in the meanings thing. So this is especially created for false Asia this year. False Asia this year is 2018. And this is what we call a crypt arithmetic problem. So crypt arithmetic means we don't know what the letters stand for, but they stand for some digit between 0 and 9. So the first FOSS is some four digit number. And the second ASIA is also some four digit number. But I've changed the digit and then just show you a letter. That's why it's a sort of crypt, because I've encrypted the digits. So instead of showing you a digit, I show you a letter. And these two four digits numbers add up to 2018. So the trick, the goal of this problem is to find out what is the digit each letter represents. Does that make sense? So some of you may have seen something like send plus more equals money. That's the sort of very famous one, send more money. But this is sort of a new one just for this year. Anyway, sorry, that's my phone because my time is almost up. OK. OK, but anyway, what else? And I guess the other constraint is we usually say that all the letters have to be different digits. So each letter represents a different digit. OK, so how do we model this in Minizling then? Let's see. I also have this in Minizling somewhere. And here I'm going to show you a little bit different, a few different features. So first of all, we can also include other files, just like you can in programming languages. We can reuse certain things that have been defined elsewhere because there's a lot of constraints that are very convenient and used in different problems. So one of them is called all different constraints. I think this is 1120. I only have five minutes left. I'll go through this a little bit fast. OK, so a new feature in the recent version is something called enum. So we declare enumeration of the five letters. The sum is 2018. We know that. The unknowns is this array of letters. Here you can see I'm indexing the array by letters. We call it V. And the single is only two constraints. The one constraint is this equation, which tells us f OSS plus ASIA equals 2018. So 1,000 times V of f and so on. And the second constraint is this called all different. All different is coming from this include file. So all the letters must be different. And you just want to satisfy the constraints. So solve, satisfy. Turns out there's only one solution, actually. So nothing much to minimize and maximize. So just solve, satisfy, will do. And this is just the output line to print out the answer. Just as a homework, you can work out what this is supposed to be. Or you can write it in, actually, there's a simple way to write it in many things. You can just declare variables for every letter. So you can say every letter is a var, var 009 colon f, var 0 dot dot 9 colon o. There will be sort of the more straightforward encoding. You just declare separate variables for every letter. Here, I'm just showing you, you can also do it in this slightly more succinct way, I guess. You can just encode it using this array of letters. So let me just quickly wrap up here. That's the encoding. OK, so in conclusion, hopefully, you've found many interesting language, which will help you to model different optimization problems. And then later on, we can solve them with any of the solvers listed here. There's a really great Minising tutorial by the folks that develop Minising. You can get it from that link. And there's also very interesting Minising models you can find on this particular website. Thank you for your time. Are there any questions? Yes? You don't know how to do something for like, say, CTA support. Yeah, so I think that would be more like writing it in the custom solver language, because you're tied to that particular solver, I think, if I'm not wrong. Ah, OK, so then it's quite similar, I suppose. But I guess it depends on the modeling power of the library itself. So some things may be easier to express in certain. So I've certainly found Minising to be quite easy, because it allows things like arrays and sets, for example, which are quite common mathematical objects that you want to express in your optimization models. Yes, so that depends on the solver, actually. Not so much on the language, right? So the language does support SAT type problems. You can declare bull as a built-in type. So you can declare a model of all bulls that will, and you can pass that to an SAT solver, yeah. So if you use certain languages, actually, certain solvers can't be used. So certain solvers do not work with mixed integers and floating points. So this is called MIP, mixed integer programming. So certain solvers don't support that. So if you have models with integers and floating point, it will just say it's not supported. So like, certain solvers, SAT solvers only support flat-sing inputs with only Boolean variables. So if you declare only Boolean variables in your model, then it will work on those solvers. So you do have to be aware of the limitations of the solvers, I guess, when you declare your models. Because the translation to flat-sing is a fairly straightforward translation. So if you use Booleans, it will just convert to Booleans. If you use floating point, it wouldn't somehow magically become Booleans. Yeah, so there's also native wrappers for the minising binary in Python or in different languages. You can programmatically call the minising process without using the command line or using the ID. Ah, great, well, more questions, yes. Not so much for the language, but more for your solvers. So if you have more of these than the solver, you just get very slow. You might have to buy the proprietary solvers. That's why people make proprietary solvers because they can solve much larger problems. For the open source ones, there's obviously some limitations. But not, I mean, yeah, so it depends. It's on a solver by solver basis. So this minising language is actually neutral, right? You can use whatever, how many variables you want and so on. It doesn't really say. But when you choose the solver, you have to choose one that works well for your particular problem. Yeah? Can I just show the Boolean thing? Yeah. Yeah, you can actually write Sudoku quite easily because we have all different, right? So Sudoku mainly consists of all different constraints, but in the sub-squares and in the diagonals. Yeah, so actually quite easy to write with all different constraints. Yeah, it's actually, but it's longer than one slide, which is, I wanted to show Sudoku, but it's just longer than one slide. It has like a few different, all difference, yeah. It's definitely doable. All right, I think there are no more questions. Okay, thank you once again.