 Hello, I'm Jeffrey de Smit. I'm going to show you how to use artificial intelligence in Java. More specifically, I'm going to show you how to do planning optimization, which saves some of our users up to hundreds of millions of dollars per year. Today, I'll show you how to code this school timetabling application from scratch using Quarkus and OptaPenner. This application, when we click the solve button, will generate a timetable for us. And these timetables will improve the life for teachers and students. It will take into account the number of constraints that we add to it. With that experience, you can then tackle other use cases, such as equipment scheduling, where we need to assign things such as beds in hospitals to patients. And we want to improve the utilization of those things, which could also be things like cat scanners or other equipment. We'll also be able to tackle job-shop scheduling, where we want to make items, for example, books or furniture or things like that, or cars. And we want to reduce the makespan, which is the time to create those items. Again, increasing efficiency of our factories in this particular case. You'll learn about another case you can tackle is vehicle routing. This is probably the most interesting or at least the most profitable case. This is the case where we have users saving a ton of money and a ton of CO2 emissions. And that's simply by reducing the driving time. So we've seen driving time reductions of 20-25%, which, as you can imagine on the fleet of tens of thousands of vehicles, makes a huge difference in productivity and fuel and carbon emissions and things like that. Also, bin packing is a typical use case, for example, on optimizing cloud resources and so forth. And a very favored one, of course, of course, employees rostering where we are signing shifts to employees and to take into account their wishes and their desires as about their free time and their desired time off. But today we're going to focus specifically on school time tabling. So school time tabling, in this problem, we need to assign these lessons to rooms and to time slots. So for example, we have room A and B here, and we have two time slots here, A30 to 930 and 930 to 1030. Now there's a number of constraints we need to take into account. In this particular case, I'm only showing some of the hard constraints, which is that these two lessons have the same students group. They're both going towards the ninth grade. In this case, there's only one group of students, one class of students in the ninth grade. It's a very simple school. And we need to make sure that these two lessons don't happen at the same time. The same thing here with chemistry and French, they cannot happen at the same time, not because they're the same grade. They're a different one, as you can see, but because they're the same teacher, both being given by Marie Curie. So of course, that teacher cannot be at two places at the same time. And last two here, as you can see, have again the same students group. So these are a number of constraints. Now what we're going to do is we're going to create those lessons and those rooms. We're going to give those to OptiPlanar. That's our problem. And OptiPlanar will actually give us a solution which adheres to all of these constraints. Especially adheres to all of the hard constraints and thus it's utmost best to adhere to as many soft constraints as possible. So how are we going to build this application? Well, we're going to use, as I said earlier, Quarkus for that and the Quarkus platform. And we're going to expose a REST service. And using Quarkus, we're going to use the extensions REST easy to expose at the REST servers. We're going to, of course, use OptiPlanar to optimize, to find that optimal or near optimal timetable. And we're going to use Hibernate, the Hibernate extension to store all that data into a normal relational database. Of course, if you build this yourself, you might want to replace certain aspects of this. Instead of a REST service, you might want to look at pulling from a GMS queue. Instead of a relational database, you might want to swap that out to Hibernate OMG to connect to NoSQL databases and so forth, or any other extension to connect to any other types of databases or data stores. So that's what we're going to do today. So I think that's enough talk. Let's get started. The first thing we need to do is we need to set up our project with a build file. So I'm going to take the easy route here and I'm just going to go to code.quarkus.io and I'm going to use that website to generate me a project and the build file. So let's take a look how it works. So this is code.quarkus.io. As you can see here at the top, I'm going to generate a Maven file. And I can, of course, also choose Gradle, but I'll go with Maven today. I'm going to call this, yeah, the Acme group is good. I'm going to create a timetabling subgroup there and my project will be called school timetabling. Here we go. Now, of course, we need a number of extensions, right? If you remember from the platform diagram I showed earlier, we will need to, we want to expose this with REST easy specifically and we want to, we'll use Jackson for the JSON serialization, right? As we're sending a JSON file to our UI. The next thing we need is, of course, hibernate. I'm going to use hibernate with Panache. So hibernate or M with Panache to go to the database, which makes it a little bit faster to implement all of this stuff. And the last thing we need, of course, is Optoplanar. That's the AI part. So let's go down here and that's over here. I'm going to use Optoplanar, the constraint solver AI to, as we shown earlier, to optimize the timetable. And later at the end of this video, I'll also actually send the score back to the UI to show it there. And the score is something that comes out of Optoplanar. And of course, we want that to be in pretty JSON too. So we're going to use that JSON binding for the Optoplanar score classes too. Okay, that's it. These four extensions, that's enough. Let's generate our application, right? So we download a zip. Let's open that zip file. As you can see, I have it over here and I can now extract it. So I'm going to extract it to this demo folder over here. There we go. You can show the files and close this. So what you can see here is we now have a nice POM file and we have a readme file and some Docker and some Git ignores already there. We can remove those if you don't like those. And we even can just run it with Maven. The Maven wrapper is even included. So let's open this thing up in our ID. I'm going to use IntelliJ today. So let me do that. So I'm going to open an ID over here. I'm going to go to that directory, which is in temp demo. Here's cool temp table. I'm opening that. Okay, it's opening over here. So here we go. And it's loading. You can see it's loading here at the bottom. There we go. And we have our first application here. So let's try to build that. But before we do that, let's jump into the POM file. So by default, this uses Java 11. I still build with Java 8. I'm kind of old apparently. But I'll just switch to 8 here for me. And then the next thing, of course, is I'm going to, for now, actually comment out the opt-up on our part for a minute, as we'll just focus on the domain and the rest of the database first before we jump into the other parts. Let's import these changes. And let's open the terminal. And let's build this. So we can now do maven clean install, of course. And as you can see, that works. Well, it's building. Let's see if it works. There's actually a test included in there, if you want to take a look at that. There's an example rest file in there also, right? And of course, you can build this natively too for people who are interested in that part, which I will show at the end of this presentation. Okay, now let's see what's in here. So like I said, there's an example resource in here already, which if you go to localhost 8080 hello, we will get a return back of hello. So let's try that out. Let's do maven compile quark as a dev. All right, here we go. That's booting up. Let's go to the over here. Let me just go to localhost. If you go to localhost, we can see the static website here, which is included there. And by the way, if you're wondering where that is, that's over here in the resources that this index file. And of course, we can now do hello. And we get back hello. And just to show you that this is quark as we can say, hello world. And we just go to the over here and we just refresh and we see hello world, right? This is the speed at which you develop in quarks. Okay, so time to make some changes. We leave the dev mode on, of course. So the first things first, we're going to have to, we're going to need a domain model. So I'm going to add here a new package called domain. All right, and remember we're scheduling lessons into time slots and into rooms. So let's maybe start with a time slot. Okay, time slot class. Here we go. Oh, let me delete that. I need to ask for a new Java class, not a file. So time slot class, here we go. Now what I'm going to put in here is I'm going to give this an ID. Now I'm going to choose to give all of my objects here just a plain old long ID. Of course, you can use a UUID, you can use a string or you can probably not use an ID at all if you're not going to relational database, right? But I want to actually put this all into relational database. So I'll just use, I'll need an ID. Okay, I'm going to every time slot. That's basically a time when a lesson can happen. It happens on a specific day of the week. I cannot put a date here. Why cannot I put a date? Because the schedule repeats every week. So I can just say this is a time slot that happens on Monday and it will happen this week on Monday but it will also happen next week on Monday. We're just creating a timetable for one week. Of course, it needs to have a starting time. So I'm going to use local time here, start time. And there we go. And I'm going to use, and of course it needs an end time. So let's add an end time too. Sounds great. For simplicity reasons, all the time slots here will have the same duration. All of the lessons will take the same amount of time. Now I'll need a dummy constructor and empty constructor, not a dummy constructor and no arg constructor because of this being JSON-nafied through Jackson, right? And later when I want to create some test data I will also want this extra constructor here so I can just create them easily. Let's add some getters. This is Java, of course. So let's just do that. And I also later will show you how to how this shows up in the log if you want to look at what the opto planner is actually doing. I'm going to add it to string here so that log is really readable. And the key there is if you want to really easily read those logs that's a personal hint of mine is to keep this short. So what I'm just going to do is I'm just going to take the day of week and I'm going to add the start time. And that's it, right? Clean and simple. Okay, that's our time slot class so the important thing about these is besides the idea of course we can add time. So for example Monday from 9 to 10 or from 10 to 11. Okay, next class. Next domain object. The next one is the room. The room is much simpler. So a room will give that an idea of course and will give that a name and of course it needs again an empty constructor because of the Jackson thing and it also needs we'll want a constructor for a test data which just takes the name and of course we want some getters because this is Java and then for the logging I would like to override to string again and just return the name. And here we go. That's the room. The next one is the lesson. So let's create a file for the class for the lesson so we'll be assigning multiple lessons. And here's where it gets a little bit more interesting. So first things first of course. Let's give it an idea and let's a lesson has a subject. Like this is a lesson for math. This is a lesson for French. This is a lesson for Spanish. Listen for English of course. Each lesson also has a teacher to keep things simple I'm going to use the string for a teacher which is just a teacher's name. Of course in a more elaborate example you would actually create a teacher domain class and you would actually just reference a teacher domain class here and that's of course all possible. I'm just going to keep it simple here. And of course we need a student group. The class of students that are attending this lesson. And you'll see in the example I'll simplify that to simply the grade the 9th grade or the 10th grade. It's a very small school that I'll show here but you could just have one 9th grade the math class or the STEM class and 9th grade the art class so you can actually split those up. And of course these classes could actually share teachers who do lessons across multiple students groups. Now on top of this each lesson needs to occur in a specific time slot and in a specific room. And those are a little bit special so let's first add those. So in a specific time slot this is when the lesson happens and this is the room where it happens. Now the interesting thing about these is that these are not known in advance the time slot in the room that's actually Optoplanar's job. That's the AI, the artificial intelligence job. So this changes during planning. This is actually initialized during planning. Initialized and even changes initialized and even changes during planning. Good. Again we want an empty constructor this time it's not just for Jackson, actually Optoplanar will need an empty constructor here too. And of course we still want the subject teacher and the student group to be easily set for test data later so I'm going to create a constructor like this here. Now let's create a bunch of getters for all of this stuff. Here we go. There we go. And again I like to have a good two strings so I can watch out in the logs what's going on. So what I'll do is I'm just going to return the subject here. Now the problem is that the subject itself is not unique. So for example the math teacher can actually do multiple can do will actually for the same teacher might actually teach multiple lessons to the same student group. Just adding teacher or student group won't make it a unique either. So if all else fails let's just add the ID. So I'm just going to add the ID just so we can distinguish between different lesson instances that have the same lesson the same subject of the same teacher for the students student group and in that case we can differentiate them in the log at least by the ID. And if you actually look into the application you'll see that the ID is slightly grayed out there on the bottom right of each lesson for this debugging purposes. This is purely for debugging purposes. In a real UI you would hide that. Okay looks good. Now we'll have a bunch of lessons we'll have a bunch of rooms, a bunch of time slots. We'd like to wrap this in a data set which we can just send to the UI in one go. And also give it to opto planner in one go and so forth. So what I'm going to do is I'm going to create a timetable object and so a timetable object has basically all of the information for one particular school, for one particular tenant so I'm going to use I'm going to give it a list of time slots I'm going to give it a list of first rooms right room list I'm going to give it a list of lesson lesson list and that's enough for now. I'm going to give it an empty constructor again both for Jason as well, Jackson as well as opto planner in this particular case and I'm going to give it a constructor where I just for when I create a test data later on. Okay let's add some getters here we go yep and on this I really don't care about it too because this is not showing up in the logs so I'm going to leave it out we had an example resource here that came with the example I'm going to throw that out. It also means that of course I'd better throw out these tests so let's do that and let's add an extra package here for the rest stuff here we go for the rest stuff now we have our domain objects and now we want to expose this through rest methods so we can actually see those happening in our browser so let's create a timetable resource here we go a java class java class timetable resource what do we want to do in this timetable resource basically we want to add a method here public timetable get timetable right and then we'll implement that in a minute and we want to make sure that's exposed through rest so we're going to add a path here for let's say let's use a dash timetable we're going to add some producers producers here this is purely going to be media type application let's first inject it first take that alright okay we want to have a time a media type of application JSON and we want to make sure that anything we get from the service also is JSON so it looks like a good start one thing's missing of course we need a method together so what I'm going to do here is I'm going to add a get method here okay now we've exposed this timetable thing through rest it accepts JSON it consumes JSON and it's a simple getter and you get this information back now what do we want to get back here we want to create and we're going to get back a new timetable in the end of course this will need to come for the database but for now just see if we get this rolling so far I'm going to create a dummy one so what I'm going to do is I'm going to create a dummy time slot over here no time slot for the day of week Monday for local time of here we go of 8.30 seems like a good start for the first lesson and local time of up to 9.30 so this is the start time and the end time of this particular lesson alright okay we're going to create a room also here we go no room let's just call it a room A and we're going to create a lesson lesson new lesson and this lesson as you can see we need a subject teacher and student group so I'm going to give this lesson this is called the math lesson the best person to give this I guess is probably there's others of course in history but let's go with Alan Turing and the student group let's just call this the ninth grade there's just one class in the ninth grade it's a simple small school so now we need to put this into this timetable so I'm just going to use collections singleton list of the time slot the collections singleton list of the room and of course collections singleton list of the lesson here we go let's see so what we just simply do is we jump here we refresh the hello resources of course now gone we look for the timetable when we ask for the timetable we get our pretty Jason thing back and you can see this is all gone through Jackson and this is all looking pretty okay the idea of course was null because we didn't fill that in the day of week Monday and so forth this is pretty ugly to look at right it's not really visual to look at right and so the next step is of course we want to have a pretty UI interface right and we're going to put that here we're going to put that in this file here let's get rid of the default index HTML now I could write you the HTML in the JavaScript here unfortunately when I write JavaScript I don't really like JavaScript it's not really my language and it would actually turn this video into an R rated video which I don't want so I'm just going to copy the UI in this particular case as it's potentially less interesting so I'm just going to copy in here three files these three files are the HTML file which shows the pretty UI the GS file which handles the buttons and so forth this is using to the bootstrap and things like that and a logo because I like showing a logo for planner because that's my project right so let's see what happens now so the rest servers of course hasn't changed but if I now go to 8080 you can see I have a pretty UI now what do I have in this UI as you can see we have our room here we have our time slots over here and it looks pretty okay we have our first lesson here Matt by Alan Turing 9th grade you can see there is no ID here because the ID was null still this hasn't been in a database or anything like that yet and when we click the solve button we get a big pretty error message of course because all of that stuff hasn't been implemented yet we cannot also not just add a room let's say add a room D here submit the room that all that stuff doesn't work of course so it's time to actually implement that stuff so let's go back the next thing we need is we need to make these things JPA friendly so let's start with the time slot this last time so if we go to the time slot what we're going to do is we're going to first make sure so this is making them hibernate friendly we're going to put them into the database is this is an entity something we want to put into the database of course anything that goes into a relational database needs an ID and specifically we're going to use the IDs will be generated on the database so I'm going to just put that to strategy generated auto here so we get our database IDs you could then also add not null annotations and so forth I like to do that myself but I'm going to skip that here so that's for time slots and now let's take a look at the room we're going to do basically the exact same thing so let me just even just copy that because it's a bit lazy as a program right so and of course we need an entity here again that this is also something that goes into the database same story for a lesson this goes into the database here and there of course it becomes a little bit more complex because even though these are all normal properties the ID is a bit special but over here you can see there's some red lines already yeah this is not a basic attribute right so what we need to do is we need to tell hibernate okay this is a many to one relationship right every lesson is assigned to exactly one time slot one time slot has multiple lessons assigned to it hopefully not in the same room at the same time of course and room same thing one room will have multiple lessons but hopefully not at the same time and but it could be at the same time temporarily right could be like this in the database for a little bit of time at least so we've done this looks good now if we start this up what we will see of course is this needs to go into the h2 database right so if you one of the things we've added oh and I forgot to add that actually I didn't add I didn't select when generating the code I did not select h2 database now that's not a real big issue because I can just say take this one and and I'm just going to add the quarkus JDBCH 2 and there we go right we import this you can see something going on here at the bottom it kind of woke up and did that and for that to work we need to have a little bit of application properties so the application properties is a file with key value where we can say where you set all kinds of things for the extensions so what I'm going to do here is I'm going to for the database part so let's do this database part what I'm going to do is I'm going to say okay quarkus.data source dbkind is h2 so we're going to we're using the h2 database in memory right now and then for the username I'm going to just leave that empty the password I'm going to also leave that empty and the data source JDBC URL is actually so we're going to use JDBC which means that we're going this is memory database which means that every time we restart so let's call this time table school time tabling school time tabling and so every time we restart because of the in memory aspect here we will lose all of our data that's not what we want to do in real time in the real world of course but it's great for this first implementation and quickly developing through new versions without having to actually set up a database server so I love this approach okay so hibernate or m database generation what I want to do is every time every time quarkus here starts up I want to drop the entire database and recreate the schema in the database based upon the lesson so you notice here I don't create sql statements to create the tables or anything like that hibernate does all of that for us based upon these at entity annotations and specifically also those many to one annotations also notice that the time table is not in the database so the time table does not go into the database right so it doesn't have an ID it doesn't have that entity annotation we're just storing the time slots the rooms and the lessons those three in the database okay so let's take a look what we have so far if we jump back over here we refresh this this doesn't crash that's always a good thing you see here no problems here at the bottom you can see that hibernate and so forth is active but are we sure this actually works that we now have a database schema and so forth so just to make sure that all works I'm going to create some test data here so I'm going to add a new package I'm calling it the bootstrap package and in that one I'm going to add a demo data generator so that's something that will generate a bunch of demo data for us and the way I'm going to do that is I'm going to first of all create a method here which is generate demo data and so and here we're going to create of course the data to generate but before we do that we need to make sure this is picked up so I'm going to create it an application scope that basically means that there's only one instance of this and then I'm going to say okay in a transactional method because of course when we contact the database we want this stuff to be transactional I'm going to observe the startup event so basically when the Quarkus application starts up we want to listen to that and when that happens I want to put stuff into the database okay let's put some stuff into the database let's do list time slot alright there you go and we're going to create a time slot list we're going to make it a new array list alright and in that we're going to add some stuff like add new time we can go back to our timetable rest method there we can actually copy paste this the new time slot that we created there and then we can do the whole bunch of more times so this is one for room also and in the end we need to store all of these so what we're going to do is let me forward jump into the next one let me finish this the time slot dot persist method there is no persist method on time slot so how do we get this well we actually used as you can see here RM Panache Panache right and the nice thing about using Panache is that we can now use these classes extend Panache entity base so if we go back to the time slots and we make this extend Panache entity base we're going to get a whole bunch of stuff free out of the box right so I'm going to take this and also apply that to room and to lesson and now all of these things have been Panache enabled and why do we want that because now we can actually call Panache persist give it a list of time slots and it will start this and of course we can add more here we can make one for 10 o'clock and one from 10.30 to 11.30 and so forth right and again we can do the same approach for rooms where we do where we call room persist room list and we can do the same thing for lesson where we do lesson persist lesson list and that way we can create those now it will take a bit long to actually start adding all of these test data but I like to really test this with more than just one or two or three or even four lessons so what I'm going to do is I'm going to just copy some stuff over here I'm going to place these in there so this is I'm going to create 10 time slots 5 for Monday 5 for Tuesday first lesson starting at 8.30 last lesson starting at 2.30 and ending at 3.30 in the afternoon for rooms I'm going to do the same thing it's a little bit simpler I'm just going to create a room list with three rooms A, B and C and for the lessons I'm going to create a bunch of lessons for the 9th grade so this is a lesson list I'm going to create a bunch of lessons for the 9th grade 10 of those math, physics, chemistry, biology you can see different teachers there but the same teachers are also giving lessons to the 10th grade so I'm going to add a 10th grade in there and what you can see here is that now we have two math lessons for the 9th grade, three math lessons for the 10th grade but this L entering now is giving up five lessons it's giving five lessons across the week and that will be interesting to see because apparently teachers cannot be in two places at the same time so that will be one of the hard constraints that we'll need to take into account later same thing for the student groups over here like the 9th grade, they can only attend one lesson at the same time so we actually have three hard constraints already room conflicts teacher conflicts and student groups conflict and later we'll also see some of the soft constraints to actually make their lives better of these students and these teachers ok so that looks good now we need to make sure we actually fish that out of the database over here so let's take a look at how we can do that so we can do a find by IND so what we can do is we can simply say you can make this replace this and what we can do is we can say ok return me a new timetable just like before but I'm going to take time slot because this is a panache entity and I'm going to list all of those time slots I'm going to do room list all and I'm going to do lesson list all ok and if we now do this let's make this a little bit cleaner if we now do this and we start over here what happens we see all of the rooms we see all of the time slots ones I just spoke about and over here we have all of our unassigned lessons right we have those math as you can see here is a math lesson from Turing and then of course we have all of the other lessons like physics from Curie history from Mr. Indiana Jones biology from Darwin of course and so forth ok we can if we look by teacher we can see the 5 teachers over here but we don't know who will teach we know who will teach which lesson we just don't know when they will teach those lessons and we of course by student group we can look at them too ok and notice how the IDs are now visible in the thing so each lesson actually has a database ID which was generated as they were stored into the databases great let's go back to our implementation what's the next step well we have well I would like to really have these buttons working here add a room and so forth this still doesn't work so what I'm going to do for that is I'm going to add a specific class here and let's just do room today so we do room resource alright and so this is a little bit more rest methods again we're going to do produce and consume JSON so I'm just going to copy that over this will be called rooms I'm going to make this transactional why because all of the methods on here I want to be transactional and so one of the things there is the add and delete room so I'm going to create a response where I add a room here we go and so we simply do dot persist room and that's the panache approach right and I'm going to return response accepted in fact we've accepted that room and we built that so we're basically going to return the same response and say yeah it's all good and of course this is transactional because of that now we do need this to have mention that this is a post method as we're changing data for security reasons this needs to be a post method of course and through a similar approach we can add a delete and so forth but I just want to show you we're going to skip that for here but you can see we can now actually add a room submit a new room right and if we refresh as long as we don't change any code that thing is there however if we change something here in the code let's add that delete method anyway so let's do public public response delete and we're going to just get a room ID here to delete and that's going to be of course a delete method and the path will contain the room ID and we're going to here have a path param of the room ID ok and then we're going to just say ok room based with the panache thing find by ID have a find by ID here we're going to use the room ID and we're going to say ok if we don't find it if room is null what we're going to do is we're going to return the response status that the response status is not found right so let's take a look at not found here here we go and we're just going to build that otherwise we will of course delete the room that's the method we get from panache let's make sure we do a decent comparison there and we're going to return the status that the response is ok response status will be ok ok now as I said as I showed earlier if we switch over here to the browser last time when we pressed refresh the room ID didn't disappear because there were no changes in the code this time when we pressed refresh the room ID does disappear because there are changes in the code and of course now we can still add room D that still works and now we can actually remove rooms 2 like room C and so forth as I show here perfect it's time to add some artificial intelligence to this right because it's great we have our timetable we have our rooms but we still don't have these lessons assigned and that's where the magic comes in first things first let's enable opto planner we go back to the POM file we throw in the opto planner extension and as we want to send that to the UI we're going to also add the opto planner Jackson thing in there and so what do we need to do and you can see it's already complaining about a couple of things right now the extension still really likes you to only be used when you have the proper annotations on those classes and you can read at the what the problem is so let's fix that new versions of the extension won't have this problem so I'm going to make the planning the lesson of planning entity what does this mean so first of all I'm going to refresh intelligent so it actually recognize planning entity and why the planet so why the lesson that's actually the that's a very important question a very important key concept these lessons are the things that change during planning so the rooms the time slot don't change during planning but the lessons actually change during planning they change from where they are now and assigned to an actual assigned to a room and to a time slot so because of that reasons we're going to say okay the lesson is a planning entity now the next thing is of course what of these lessons will actually change and like I said earlier these things change right we've annotated them already so what we do is we're going to just say the over here we're going to say well the time slot there that's a planning entity that's something a planning variable that's something that autoplanar can change for us right now the planning entity always needs a value provider ref and we're just going to call this a time slot range it needs to know which time slots can I put into there right and it's going to do this based upon this ref to a value provider which is something that will give me a list of time slots where we can choose one for each of the lessons to boot into that's why we need the time slot range here and the same problem for room so for again for room we want to say it's a planning variable but then I can change it it needs to change it from null to a good room and of course we're going to it needs to know where is my list of rooms where I can choose from so again we're going to make a room range here and you'll see a bit later where we actually provide those lists of time slots and those lists of rooms right but that's it for the this side for the lesson so that's now planning enabled however to give something to opto planner we need to have this wrapper object and this timetable is actually the it's called a planning solution and it's actually the ideal thing here so what is a planning solution it's basically a list of everything that goes into opto planner a list of all the time slots all the room lists the lesson lists it's also going to help us provide that list of time slots which will need to pick from fill in into these time slots field and every time slot field of each of the lessons so that's this time slots table thing and in opto planner terms that's called a planning solution so let's add a planning solution annotation there and the lesson list is where is actually all our planning entities so I'm going to tell him that this is a planning entity collection a property that has a planning entity collection this lesson list you can do this on the getters but I'm using it here on the fields as that's easier so remember a minute ago that for this time slot list we needed to give that so what I'm going to say here you can choose from that for to fill in those planning variables I'm going to create this make this a planning value range provider and so the idea I'm going to use is exactly the same one as we had here on the lesson as here is a time slot range that's the one I'm going to use here so now when we give opto planner a time table where there's a list of time slots then he will pick all the list of time slots to put into those lesson time slots fields we're also going to do is we're going to do the same thing for rooms right so then basically any of these rooms that we have in here can actually be filled into the lessons room field right now the difficulty is not assigning one lesson the difficulty is assigning all of those lessons in a way that they are compatible with each other and that's of course the hard part that's where it's incredibly difficult to do that the search basin is incredibly difficult and humans are actually pretty bad at it but that's a different conversation for another time so let's just get this implemented now one more thing I'm going to later I'm going to build constraints and I want to make sure that so these lessons are automatically available in those constraints but the time slots and the rooms are not by default just to make sure that they are available I'm going to annotate them with the problem fact collection property which basically means that these these are called facts then anything that we can use from the constraints you'll see that once we start implementing the constraints why this matters let me maybe first indeed start with the constraints now they were talking about those so I'm going to create a package specifically for the solver part of this and I'm going to here create a class file called the timetable constraint provider so that will define what are your hard constraints what are your soft constraints what do you want to optimize what are your business goals what are your how do you want to improve your return on investment how do you want to improve your employee retention how do you want to influence your service quality and things like that that's all goes into this class as constraints now and of course you can delegate it to other classes if it becomes too many in one class now what I'm going to do here is I'm going to implement a constraint provider here we go and that has a single method the defined constraint methods and for now I'm going to leave that empty leave constraints here here just return a list of empty constraints okay and we have a constraint factory that's the object that will help us create constraints but for now I'm going to say there are no constraints problem solved okay now let's get back to the timetable as soon as these lessons in this planning solution are being assigned to rooms and time slots then that has a specific score a score means what is the quality of the solution right the score actually tells you how many hard constraints are broken how many soft constraints are broken so we need to have that in here and for that we're going to add a field called hard soft score a field called score we're going to use a simple hard soft score opto planner has many other options here if you use more levels or if you need to use if the numbers become bigger and so forth but this is the simple easy approach and we're going to say okay this is our planning score right so that basically means that opto planner will automatically if it starts scheduling things put in the score in this particular field okay so far so good one more thing we need to do we need to tell let's see what happens when we run this so when we go to the over here we will see that it probably complains let's take a look no it's over here so that's good it parsed everything right let's scroll to the bottom everything's fine so opto planner now validated that entire domain model check the planning solution checked if the planning provider fits nicely to the times old range here and so forth and so forth okay now let's expose that let's let's see this actually actually work so let's go back to the timetable resource what I'm going to do here is I'm going to inject a solver manager so a solver manager for a timetable and anything we and the job IDs there are long so this is a solver manager and let's give it this and we'll let's just inject it right wire it in so what we can now do is we can say okay I'm going to add a solve method and that's when we hit the solve button so let's do that public void void solve and when this happens we're going to call this is a post method because it will change stuff we're going to give this the path slash solve so we can just call it so it nicely fits on that solve button in the UI and then we're going to say okay solver manager here's something I want you to solve but I also want to listen and why because as opto planner finds new best solutions better and better solutions I want to show those in the UI so I'm going to solve and listen so first of all we need to give it a problem ID as we're only solving one problem I'm just going to hard code one here that's the problem ID for now and let's maybe put that in in constant so this is let's call it a singleton time table ID or something like this okay anyway what we're then going to do is we're going to do to get the solution we're just going to do okay get time table sounds like a good idea that's where we load the solution from right the latest version before it's all before it's solved and then when it is solved let's call a save method of course we will need to implement a save method now so let's do that okay we can add a protected method here to do the save of a time table here we go so what's the problem here of course we need to actually provide the ID so let's also it's actually taking that table ID and giving it to this method so we need to create a method that returns a time table and that does let's do find by ID or something like this and let's give it a long ID and that ID will always be this constant now because that's the only job that we're submitting and now we can actually just call that over there so what we will do is we'll just take the code from our get method and put that there and over here we'll just do return find by ID of that singleton ID to get method into that find by ID so loading from the database is pretty much the same right we still just load all our time slots all our rooms into a lesson into there game over just to make sure that all happens in the same transaction we're going to make this transactional if you don't do that that can actually cause issues because then the lessons over here if they're already assigned to a room of our time slots which will happen if you want to do repeated planning we'll be pointing to different instances of then these time slots and rooms that are given here if you don't have the transaction annotation so by making this into one transaction thanks to hibernate these will all be you know there will only be one time slot instance on Monday from 9.30 right given that there's only one in your database and that will avoid issues okay so save wise what we're just going to do there is we're going to iterate through all of the lessons in the timetable so we're going to ask for the lessons here we go and we're going to just say okay do this isn't find me the actual lesson in the attached lessons so gpa so find me the one that's actually in the database the same as in the database that's the lesson good idea here right so there's the same lesson as the one we have here but this is actually the attached lesson and this is an attached lesson and of course you need to make this transactional so what does it mean attached means that it's in the gpa context that means if you make change it with it it will be stored and what we're just going to change in that is the time slot and the room so what we're going to do is we're going to say okay that's the lesson set your time slot set oh we don't have setters there we need to go to lesson for for a minute and actually add the ability to set these things so let's add a setter for the time slot and let's add a setter for the room right things we were missing let's go back here we go we're now going to say okay give me your lesson get so we're just going to take the original lesson time slot which is still attached but the lessons are not because they have been changed and so forth and we're just going to use that and then we do the same thing here for room here we go and now we have it saved so let's take a look at that so let's see if we have everything right when the solve method is called we're going to submit a solve job this solve job will load the time table from the database and then we'll save it to the database every time it finds a new and better solution it's going to save all of those lessons in the database their time slot and their room changes only okay fair enough one more thing we need when Optoplaner is solving we need Optoplaner needs to know how long it can solve there's many various options on how to do that I'm going to make it simple for now I'm going to use the just say okay terminate when you have a certain amount of time met I'm going to put it on 30 seconds so it basically means that Optoplaner will keep solving will keep finding better solutions for 30 seconds after which it will stop you can leave it running all the time you can leave it running until it hasn't find any more better solutions for some time there's many many options there you can terminate it from another thread as what you will see later okay back to the code here we go let's click the solve button moment of truth yep it's solving and it assigned everything to the first time slot in the first room that's not really a big surprise why because we have no constraints so it can do whatever it wants there is nothing stopping him from assigning everything to the first time slot in the first room so let's go back let's go to the constraint provider right because in the constraint provider that's where we allow us to write to add hardness of constraints so the first thing we want to add is we want to add a room conflict where we say okay let's do that here we go create the method room conflict so it looks good and what we're going to do is say okay given the constraint constraint factory if please select me a lesson this is pretty much like SQL now you're probably wondering why isn't this java maybe we can actually do this why isn't this just a java of four loops and so forth where we just get the timetable we crawl across the time slots and the lessons and so forth in that timetable now we can do that but it's going to be awfully slow because it won't do things like incremental calculation indexing and we want to do all of those things and more specifically this constraint provider will allow us to use the power of drills and Kohito underneath so we want to take use of those things to actually get solutions faster and to be able to scale out a simple java which you can do in Optoplanar you will not be able to scale out with this approach you will so okay let's do that so I have a lesson here I'm going to join that with another lesson alright but I'm going to make sure that they have the same the same time slots so they happen at the same time time slots okay and I'm going to make sure that they happen that they're in the same room let's get room so what we now have is we're basically do it's like a query but it's an incremental query and we're going to penalize that and I'll show the penalize in a minute so what we do is we're going to say give me a lesson if we find select and join that with another lesson and if they have the same time slots and if they have the same room then please continue so then I basically have two lessons here and you can see this what this returns if I would say what does this return this gives me what we call a buy stream of two lessons so that's the two lessons that are happening in the same room in the same time and this happens for any of those any such pairs and then we're going to say okay that's room conflict bad idea I don't want that we're going to give that a lesson a score let's keep it simple one heart and we can even say how that's enough right now we could even let it depend on the objects themselves the weight or we can even change another weight here like of heart or of soft to say okay I want to say this is like a very this is eight soft weights but of course room conflict is something that's absolutely not possible so we're just going to make that a simple one heart let's see what happens when we run this now so when we refresh this we click the solve button again let's see what happens now see so this looks better right because we no longer have two lessons in the same room at the same time however this is still an infeasible schedule right do you notice what's infeasible is well here you can see it touring has Matt giving the nine grade but also at the 10th grade at the same time so if you actually look at per teacher you can see we still have many conflicts there right so let's fix those so what I'm going to do is I'm going to just copy this say okay I'm going to make this a teacher conflict going to add that here teacher conflict so we now add that thing over that constraint and the big difference is that we're not going to check if it's an equal room but if it's an equal teacher right now this constraint is completely isolated from that constraint right this one the room conflict just checks if there are two lessons in the same room at the same time the teacher conflict checks if there are two if a teacher has two lessons at the same time but doesn't doesn't mind the room at all right completely ignores that and of course there's also the student groups so let's add the student group here too that's those two because one student can only have one lesson at the same time so the same logic there here you get the student group and we can call this the student group conflict and we go back to carcass we just click the refresh button click the solve button and let's see what happens we get a different schedule okay why again it looks good no room conflicts but this time there are no teacher conflicts because now Alan Turing gives Matt you can see always one room at a time you can already see what we want to change here this is not that perfect soft score wise I'll talk about that in a minute let's take a look at the student groups looks good the student rate has one lesson at the same time so that conflict that constraint is also working very well teachers right teachers hate this this Alan Turing is really hate this schedule because why he has to show up at 8.30 do a lesson for one hour and then he has to wait for an hour do another lesson right so this is really a terrible schedule for that poor guy same thing for Indiana Jones here lots of gaps between those lessons that's no fun for the teachers so let's see if we can fix that so let's go back to constraints let's add an extra constraint and let's call it the teacher time efficiency constraint right so the teacher time efficiency constraint or team efficiency and of course we're going to give the constraint factory here we go now this one is quite different than the others because it's not just looking for conflicts what we want to do is we want to see if there are two lessons over here and they are sequential I that's a good thing that's something I want to reward or vice versa if I see a gap in between them I want to penalize it but that's a bit more difficult because like these the lessons might not always sequential lessons might not always start at the end and start at the same time there might be fixed breaks in there like recess and so forth or like between Monday and Tuesday when the school closes and the school opens again of course right so what we're just going to do is every time you have two sequential lessons we're going to reward that so we're going to say okay from again lesson I'm going to say okay we're going to join again any other lesson right and this time we're not going to look at the time slots that's the big difference what we are going to do is we're going to say joiners if the lesson subject equals if the lesson sorry the lesson teacher equals right if it's the same teacher that's the ones we're going to pull out right then we're going to do a number of things with those and then in the end we're going to reward those for the teacher time efficiency and this is not a hard constraint what that means is that we can break that and we still have a feasible schedule because if you make this a hard constraint it's very unlikely that we'll be able to get a schedule which breaks no hard constraints so we're going to say okay this is of a lower priority than these right and in this case it's not just a lower priority with a lower weight it's really a lower level so it's not even we could set these to the other ones to 10 hard and this is just to 1 hard but nope we're going to do differently we're going to actually put this in the soft level and basically a million or a billion soft does not outweigh 1 hard the difference is basically infinity okay now this alone is not enough because we will have plenty lesson pairs that have the same teacher now what we need to do is we need to check that if we have to sequential lessons that's a good thing right so we need to we need to I'm going to filter on these this is something we cannot do with joins so we're going to use filters with a little bit less efficient so if you can do a join instead of filter always do a join but anyway so we have two lessons here lesson 1 and lesson 2 and what we're going to do is we're going to say okay I want you to run a predicate here and the predicate is I'm going to ask for the duration between those two lessons so I'm going to do duration between lesson 1 get time slot and of course we're going to do lesson 1 get time slot get start time get end time let's take end time let's take end time because that's the first lesson so the second lesson needs to start after this and then lesson 2 get time slot get start time right so that's the amount of time between the first lesson and the second lesson when the first lesson ends and the second lesson starts if the second lesson is before the first lesson then between is going to be negative so we're going to make sure that that's not the case so we're going to rule those out we're going to fill those out and then we're going to check okay so the second lesson is after the first lesson but is it immediately after it and we're going to basically say okay given a duration of minutes minutes of 30 right if it starts within the next 30 minutes then it's fine right so we're basically saying if the second lesson starts immediately after the first lesson or within the next 30 minutes of that then we are happy then it's then it is two sequential lessons and that's something we want to reward that's a good thing so reward means give me more of that penalize me do do less of that to optopaner so penalize will basically hit optopaner ahead when it does that and reward will basically reward optopaner give it a carrot every time it does that right there's still one bug in here of course because these lessons might actually be on different dates so what we're going to do is we're going to do here joiners dot equal now we can do is we can make a lambda for this get lesson get time slot and then get day of week we're going to just make sure that they have the same day of week in the time slots when we join them okay that's teacher time efficiency let's see if how that works out we run this we click the solve button we it still runs doesn't crash we get a quite different schedule you can see it's now changing the schedule optopaner finds better and better solutions and if we now look into the teacher thing we of course have no conflict still but we see the situation for Alan Turing has improved a lot he can actually stay home on Tuesday same for Marikari she can stay home on Monday and that's just you know nice for them right so that means much less commute much less travel and that's a good thing and of course we can see with the student group everything is still fine now we would like to see how bad what is the hard score what is the soft score of these changes that we've been doing so to see those what we can do is we can actually go into the timetable one and we have the score here but the thing is optopaner calculates the score and if you start looking at the results here optopaner sees he runs here's the solving and it will tell you what the best score is the minus 60 and yeah that's a good reason why that spin is minus 60 hard let's actually go back and fix that if you go into the thing over here what it will actually do is when two lessons of the same rule conflict it will also match for the lesson with itself so when you give the lesson math and there's the other exact same lesson ID of math will also match here and they of course the same room and they will actually be penalized so to make sure that those that that's hard score over here minus 60 hard doesn't happen that we actually get a nice zero score as we should be we're going to have to filter those out so what I usually do is I do less than for the lesson get ID right and that's a nice reason to also use a long ID works with you ID and so forth to anything that's comparable and so we're going to use that on all three tricks to filter those invalid matches out right where a lesson matches with itself as a pair and that pair gets penalized now if we run this again all right and we click the solve button again here we go here with what we will now see we get the exact same solution because nothing really changed those were all nothing what the planner could do with minimize those 60 hard constraints right they were always just there but what we look now if you look at the when it's finished it's still running but this is the result of the construction heuristic here so after the construction heuristic it broke one hard constraint and if you wait for the entire 30 seconds here it is we'll see that now we have zero hard broken which is exactly what we want and we got an 11 soft positive score so 11 times it was able to do sequential time limits so the next thing we want to do is want to make sure that the score the score over here is shown in the UI over there right so to do that we're going to go back to the timetable resource and we're going to and we want to add here in the get timetable we will also add that score so what we're going to do there is we're going to add something called a score manager which allows us to calculate the score of a solution on the side of optoplanar so what we're going to do is we're going to add score manager for timetable and here we go and then what we can then do is we can get the timetable over here and we can then say okay I have the timetable score manager please update the score and to this instance you got from the database because this is coming directly from the database optoplanar is not involved there and we chose not to persist the score into the database of every solution so we will just calculate it on the fly every time we give it to the UI every time the rest method is called we'll look what is the timetable in the database and we're going to calculate the score of that and give that to the UI and optoplanar will not be involved directly just the score manager will be so when we add this which we can do we refresh over here we will now also see the score once we solve here we go score still 0 no we don't, interesting let's go back a second what's going score manager update the timetable and we return that timetable and we inject the score manager let's go back to the timetable over here we have a planning score here and we have interesting now in the quick start example this does work so I must have made a stupid mistake somewhere my apologies for that take a look at the the quick start code to see how that's done properly okay right now we have our solution so we still notice that the solve button doesn't work correctly it immediately jumps back to solving while it is still solving in the background so the way we can fix that is if we go into the timetable resource over here we can actually add on the timetable we can add a solver status field just to give that a long so let me just add that here and the solver status is either that it's going to be solved it is currently solving or it is not solving anymore so let's add a gather for that solver status maybe the lacking of the score was the problem the gather here that could be because this is indeed that could be the lack of the score here was the problem anyway let's go to the timetable resource and let's actually set that solver state so the way we do that is we can actually get the solver state by just saying okay solver manager get your solver state are you still solving yes or no and we do that before we do any of the other things to avoid a race condition actually so we can ask the solver manager are you solving yes or no and then we can say okay on the timetable I'm going to set the solver status so we need to go back to the timetable and make a setter for that we make one for the score 2 here we go and then we can do set solver status and we can give it the solver status here we go and we can now simply go to the UI and what you will now see is when we click the solve button first of all the score is now showing up so the set score was actually the part that's missing didn't go through JSON well but even as we get new solutions this button still remains red so the solver state is now known at the UI and that's why we wanted to put that into the JSON object too now if you wait 30 seconds you'll actually see this red button jump to green because as it goes looking for new solutions what we'll see at some point is that the solver state is changed as it happens right now okay and this is our solution and you can see 0 hard broken 11 soft there's a bunch more of soft constraints there's a few more actually in the quick starts if you look into that things such as that to improve the lives of students which we haven't done so far but for to improve the lives of students you might not want to start Monday morning with two math lessons right after each other so the same lesson right after each other is probably a little bit too much so you can add constraints to avoid such cases right specifically for the students other interesting things for the teacher are the room stability so just to make sure that they're always teaching from the same room they have basically have their own room which is good so they don't have to move between lessons and things like that and you can you can probably imagine much more from your time in high school yourself how you could how the schedule could have been improved much better for you like for example no swimming after lunch and stuff like that okay now if you want to try any of this out try it out like just follow this video of course but you can also just read the guide for this right so there's a you can find more information on autoplanet or itself of course but if you want to specifically try it with with quarkis take a look at the link below the guide is there which has a link to the quick start example which has all the code I've shown you here and also includes things like tests and so forth so thank you for listening and if there's any questions don't hesitate to ask them below this video