Optimal job scheduling for a medical clinic - optimization

I am seeking an optimal solution to the following problem.
I have 3 junior medical officers (MOs) and only 1 consultant. The clinic starts at 0800 HRS; there are 12 patients in total to be seen; and the last patient should ideally start his appointment at 1100 HRS. In addition, all 3 MOs must start seeing 1 patient each at 0800 HRS.
Each patient must be seen in the following order: first by a medical officer (MO), followed by the consultant. A patient may experience a waiting time (T) between being seen by a medical officer, and being seen by the consultant.
The objective is to reduce the average waiting time (T). How then should we schedule the clinic? This problem is also illustrated in the attached Table.
I suspect that this scenario can be treated as a flow shop scheduling problem - all suggested solutions will be greatly appreciated.
enter image description here
Thanks very much!

This looks like a form of Job Shop Scheduling (so optimality will be hard when scaling out because it's NP-complete/hard). You're basically assigning each job (= 1 appointment of 1 patient) to a machine (= 1 medical officer). On top of that, you have job dependencies (because the 2 appointments of a patient on the same day must be in a certain order).
A typical approach is a Construction Heuristic followed by a Local Search (such as Tabu Search). There's a bunch of solvers out there that can help you solve this with such algorithms. For example in Java, there's OptaPlanner that has 2 examples similar to this: Project Job Scheduling and Nurse Rostering.

Related

Customer Schedulling & route optimization

I'm new to ORTools and I need some guidance on which algorithm to choose for this problem:
(I think it is an assignment problem, but also a mix with route optimization and VRP..)
A salesman has N shops to visit
Every shop has a service time T (ex: 30 minutes)
Shops are categorized this way:
a) Category 1 = must be visited twice a week
b) Category 2 = must be visited once a week
c) Category 3 = must be visited biweekly (every two weeks)
d) Category 4 = must be visited once a month
A shop cant be visited 2 days in a row
The salesman should work up to 9 hours (1 hour for lunch)
The salesman should visit 8 to 12 shops a day (soft constraint)
The salesman should work up to 176 hours a month
Some shops can be discarded from the solution. (prefer to keep the ones with lower category)
We have the geolocation and a time distance matrix for the shops
The objectives are:
Maximize the number of shop visited
Minimize the KMs for the salesman
This is an example of shops distribution
First of all, it seems like you're trying to solve the problem globally, i.e. to find an optimal monthly or yearly schedule for the entire problem. I suspect that might be difficult, and suggest to try relaxing the problem, say: to a daily one.
For instance, maybe for a given day, it's possible to compile a list of stores which should be visited, perhaps by order of scores. So maybe a store that was visited yesterday will not be in the list; and one that has to be visited once a week, and was visited 6 days ago would be on the top of the list.
Then, you might be able to solve using a variant of the VRP solver on that list; you can use simulation (maybe with some random element) to generate a weekly or monthly schedule based on that. It's not guaranteed to be optimal, but might be good enough.
If your problem is more complex then what the OR-Tools VRP solver can handle, you can try using the more general CP-SAT solver, which is very generic, and can handle VRP-like problems with additional constraints, e.g. Scheduling problems or other constraints.
You can also consider using other approaches here, e.g. Tabu search.

vb.net calculating booked appointment times from a list of available times?

I'm trying to find an efficient way to calculate the booked times for a user(object), given a list of free/available times for the same user\object.
I have an object that will return the "available" times for a given specific day. The duration/end time is fixed to 10 minutes.
Example Starting data:
12/23/2020 8:00 AM
12/23/2020 9:00 AM
12/23/2020 1:00 PM
In this case I need to generate the "unavailable" times and insert them into a database with a fairly simple schema:
start_date | end_date | start_time | end_time
The inserting is fairly trivial, i'm having a hard time determining the best way to calculate the unavailable timespans.
Using the example above i would need to generate the following timespans:
12/23/2020 12:00 AM - 7:59 AM
12/23/2020 08:11 AM - 8:59 AM
12/23/2020 09:11 AM - 12:59 PM
12/23/2020 1:11 PM - 11:59 PM
Any frameworks or libraries that can do the heavy lifting on this for me? Is it possible to solve this problem without looping through the results and calculating all of the offsets?
To anyone asking "why" - hooking together two legacy systems, one system returns the available appointments for a given date this needs to be plumbed into a system that needs the un-available appointments for a given date.
Well, first I written more tour booking systems then I can shake a stick at.
The one Rosetta stone that holds true?
You don't want to generate or have booking slots that are NOT being used in the system PERIOD!!!
Thus you ONLY ever enter into the system a valid booking (starttime, and end time). And that startTime should be a datetime column - this will VAST reduce the complexity of your queries. Given you have date and separate time? Well, then your queries will be more complex - I'll leave that to you.
Given the above? The simple logic to find a booking collision in ALL cases is this:
A collision occurs when:
RequestStartDate <= EndDate
and
RequestEndDate >= StartDate
Now in above, I assume date values, or datetime values.
So if I want a list of any booking for today?
RequestDDTStart = 2020-12-23 9 AM
RequestDTEnd = 2020-12-23 5 PM
And thus any collision can be found with this:
strWhere= dtRequestStartDate <= BookingEndDate" & _
" and dtRequestEndDate >= BookingStartDate"
Now, assumging .net, then above would be something like this as parameters
strWhere= #dtRequestStart <= BookingEndDate" & _
" and #dtRequestEnd >= BookingStartDate"
So, above would return all bookings for today 9 am to 5 pm
A REMARKABLE simple query! Now of course the above query could/would include the exam room, or hotel room or whatever as an additional criteria. But in ALL cases the above simple query returns ANY collision for that 9 am to 5 pm.
And the beauty of this system? As long as you never allow a over-lap into the booking system, then you can book a 10 minute or a 20 minute or a 30 minute session as ONE entry into the database. I would thus not need to create 3x 10 minute slots.
So, this means you NEVER have to create booking slots. The whole system will and can be driver with a simple start + end booking record. And as noted, then you can book 1 hour, or 40 minutes. Your input (UI) can simple limit the time span to increments of 10 minutes - but that's the UI part.
Now I suppose to display things in 10 minute increments on a screen? Well, then you would have to submit 6 requests per hour to "display" the time slots. For a whole day, that suggest for 9 am to 5 pm, you would have to run 8 x 6 = 48 requests to get a list of 10 minute increments. But then again, you COULD just show the existing bookings for a day, and allow new bookings to be added - but don't allow if there is a over lap.
So, as noted, the concept here is you don't really need "slots" in the database. I suppose you could try slots, but it makes the code a HUGE mess to deal with. if you ONLY ever store the start + end? Then I can say move the booking to another day by JUST changing the date. Or I can extend a booking from 10 minutes to say 20 or 40 minutes - and ONLY have to change the end time. As long as no overlap occurs with the above simple "test", then I can simple change the booking to be 40 minutes in length - and ZERO code to update multiple slots is required. And same goes for reducing a booking from 40 minutes to 10 minutes. Again ONLY the end time need be reduced - a ONE row update into the database.
So if at all possible, I would dump the concept of having "slots" in the database. I might consider such a design if a booking was only ever 10 minutes. But if 10 or 20 or 30 is allowed, then you don't need to store ANY un-used slots in the database, but ONLY ever store a valid booked slot. Empty un-used time can thus ALSO be found with the above query. (if the query returns records - then you can't book).
So display of free time in some UI becomes more of a challenge, but showing bookings that span 10 or 20 or whatever minutes is far more easy, and as noted, you can even change a whole booking to a different room by a ONE row update of the room ID. If no collision occurs, then you allow this booking - and you achieve this result by ONLY updating one simple booking record that represents that start + end time.
and this means you also NEVER store the booking totals in the database - you query them!
I also found that if I say store any booking totals in the database? Well, with complex code, we always found that the totals often don't match perfect. So then we wind up writing a routine to go though the data, sum up the totals and write those out.
But, if you never store any booked totals (say people on a bus, or people in a given hotel), then while the query for such display is somewhat more difficult, it becomes dead simple to remove a person from say a tour by simple null out of the tourID.
So, this display shows the above concepts in Action. And the available rooms in the hotel, people booked on bus, and even totals for "group tours" are ALL values NOT stored in the database:
So in above, people booked on bus, booked in rooms, and rooms used? All those values are NOT stored in the database. And no slots exist either. So if we have a bus, then we set the capacity of 46, but we do NOT create 46 slots to book into. So be it a bus, a hotel, a medical exam room? You don't create booking slots ahead of time, but simply insert bookings with a start + end, and then query against that concept.
So, to find a total on a bus (or say in a exam room), I query to find the total for that day. And if I want to move a group booking of 4 people from one bus to another? Then one FK update to the given bus they are on allows the whole system to "cascade" the existing values in the system. And same goes for moving a person from exam room #1 to #5. You only have to update the FK value of the exam room. If no collisions occur, then this again is a one row update. If you have multiple exam rooms, and multiple slots, then what should be a simple one row update in the database becomes a whole hodge podge of now having to update multiple booking slots with whacks of code.
So you book "use" of resources "into" a "day" a "bus" a room, but it is the act of that booking that consumes the time slots - not that you pre-create records or timeslots for each "range". This thus allows you to leverage the relatonal database model, and reduce huge amounts of code - since you not coding against "slots", but only that a exam room is open from 10 am to 4 pm. That available room for that day is thus ONLY ONE record you create in the system, and then you are now free to book into that one day given room range. The bookings into that one room for the day can be 10 minutes, or 40 minutes - but it ONLY one record being added into the database to achieve this goal (booking).
Regardless of the above, that simple collision query works for any collision (including a whole overlap, inside a existing span, or even the end or start overlaps any booking. And that query is dead simple - and it works for all collisions. So I don't have a library to share, but that simple booking collision finder query can thus drive the whole system based on that kind of simple query.

How to structure reference table changes overtime

This is more of a design issue than technical though I do plan on building my report in PowerBI so any limitations of that system should be noted
I am trying to wrap my head around changes in employee information over time and a time card system.
Let’s say I get a time card report that has some standard information. Thier employee ID, number of hours, task hours charged to, date hours charged and so forth.
I also have a HR report of details of each employee which I of course would link via employee ID. This table would have information like job title, department, name, location and so forth.
I am building a report that would show how many hours are charged each month historically by job title and department.
Let’s say employee 3453 was a Janitor in June and charged 20 hours. In November he became a security Manager and charged 10 hours.
When doing reports in December using the current HR data my report would show that 30 hours were charged to the security department instead of 20 to sanitation and 10 to security.
Adding multiple employee records to the HR table wouldn’t work because it would have duplicate keys
I guess I can’t wrap my head around it.
Any help would be greatly appreciated
Thanks.
This is generally handled by working with a separate table that tracks the details of the employees over time.
For example:
EmpID Position DateStart DateEnd
3453 Janitor 1/1/2018 10/31/2018
3453 Manager 11/1/2018 null
There other ways to handle this. I recommend doing to searching and reading using the term:
"slowly changing dimension"

tour start as an additional planning variable in Optaplanner VRPTW - a good idea?

Hi and happy new year to all Optaplanner users,
we have a requirement to plan tours. These tours contain chained and time-windowed activities (deliveries) executed by a weekly changing number of trucks.
The start time of a single tour can vary and is dependent on several conditions (i.e. the goods to be delivered must be produced, before the tour can start; only a limited number of trucks can be served at the plants gates at the same time; truck must be back before starting a new tour). Means: also the order of tours can vary and time gaps between the tours of a truck can occur.
My design plan is, to annotate TourStartTime as a second planning variable in Optaplanners VRPTW-example and to assign TourStartTime to 2-hours time grains (planning horizon is 1 week and tours normally do not start during night times; so the mentioned time grains reflect a simplified calendar for possible tour starts).
The number of available trucks (from external logistic companies) can vary from week to week. Regarding this I wanted to plan with a 'unlimited' number of trucks. But the number of trucks per logistic company, that can be actually assigned with deliveries, should be controlled by a constraint (i.e. 'trucks_to_be_used_in_parallel').
Can anybody tell me, if this is a feasable design approach, or where do I have to avoid traps (ca. 1000 deliveries/week, 40-80 trucks per day) ?
Thank you
Michael
A second planning variable is possible (and might be even the best design depending on your requirements), but it will blow up the search space and maybe even custom course grained moves might become needed to get great results.
Instead, I 'd first investigate if the Truck's TourStartTime can be made a shadow variable. For example, give all trucks a unique priority number. Then make a Truck's TourStartTime a shadow variable: the soonest time a truck can leave. If there are only 3 lanes and 4 trucks want to leave, the 3 trucks with the highest priority number leave first (so get the original TourStartTime, the 4th truck gets a later one).

OptaPlanner: deliveryman going from multiple suppliers to customers

I'm new to OptaPlanner, and seen how some problems can be solved fairly easily by modifying from the very useful sets of example. I'm trying to figure out what is the best way to model my problem.
I have a group of deliverymen, and their job is to deliver supplies from multiple suppliers to multiple customers. The tricky part is that the customers requirements and suppliers supplies are range values that varies from month to month. And I also have the option to hire temp deliverymen if the supplies and demands of the month is too high. End result is to maximize profit for each month.
What category of optimization problem am I facing, and I'm struggling to find the best way to model this problem. Any suggestions?
Put in a number of temp deliverymen as normal deliverymen with a boolean temp=true, and have your score constraints penalize those more (I presume a higher soft weight as the soft score will be your profit).
This is basically the pickup and delivery variation of the VRP example. Some of our users have adjusted the VRP example to this already (see some of the other questions here on stackoverflow tagged with optaplanner). Basically the trick is to write a score constraint that understands that the "load" of a vehicle changes through its route (but it should always be less than its "capacity").
You can schedule 1 month (or 1 week or less or 2 months or more) at a time, but you can also do "continuous planning" (if months affect each other like in nurse rostering, but I doubt that's the case here) (if so, see the optaplanner video on youtube) to plan a window.