During a road trip we discussed how this tool was interesting but not necessarily useful. My friend could plug in numbers and see what happened, but that didn’t help make staffing decisions any easier, because it was mainly educated trial and error. What can be more helpful was a system where you might set a goal SLA for throughout the day and the system would offer you the very best agent schedule to match that concentrate on, given variable incoming call parameters and agent handle times. That could be a third great pillar of OR: optimization.
The difficulty was that with optimization you needed a formula to optimize, and we had a simulation. Nonetheless, it dawned on me that there was no reason we couldn’t use the simulation as the target function for an optimization problem. It takes a set of inputs, and with enough runs of the simulation, gives you a confident output. Luckily, 30 years ago researchers had the identical thought and have been studying the sector of Simulation Optimization ever since. These smart people had done all of the labor for me. [3, 4]
So, again, I set to work. Now the core of Simulation Optimization is making a set of inputs, running it in your simulation, and selecting the following set of inputs in an intelligent way until you reach your objective. My optimization problem looked something like this:
My objective is to construct a schedule. Schedules are made out of shifts. Shifts are set times throughout the simulation “day” (say 9am — 5pm, 10am — 4pm) during which an agent can work. A schedule is the set of all shifts, with what number of agents are working on each shift. A great schedule is one which keeps the SLA hovering right across the goal and minimizes the entire variety of employee hours (variety of employees * length of their shifts). Certain shifts have a cap with how many individuals can work (ex. only 10 people can work the half day morning shift) and every shift has a certain break schedule. The SLA should never cross a tough ceiling above the goal (ex. our goal is an SLA of three min but nobody should ever wait greater than 10 min).
I arrange the framework so all of those constraints were accounted for, and now I used to be able to optimize. Some problems talked about in Simulation Optimization research are difficult because their simulations are black boxes, meaning you don’t get any useful signals besides the simulation answer. Luckily, this simulation is different. We’re measuring the time-in-queue throughout the simulation “day” so we are able to see how certain shifts do compared to one another.
For instance, if the morning shift has more agents than the late shift however the late shift has more incoming calls, we’ll likely see that the late shift is more astray than the morning shift. In our next attempt we must always probably increase the number of individuals on the late shift.
It’s not so simple as this — with complicated overlapping shifts and other parameters there might be interdependency, but we are able to plug any schedule into the simulation and see what happens.
These signals that we get from the simulation led me to a transparent strategy, gradients.
Gradient Based Search could be understood as a ball rolling down a hill, where with every step you are attempting to get the ball to a lower state until it reaches the underside. That is complicated by the proven fact that it shouldn’t be an excellent hill. There might be false bottoms where any direction you choose goes back up the hill, however the true bottom requires you to start out rolling from a special location. In practice, we’ll never know if now we have reached the true bottom, but there are smart ways to make sure that we do enough tests to know that we’re pretty close. In our case the ball is a schedule, and the underside of the hill is the right line where all incoming calls wait for precisely the goal SLA.
So, what’s essentially the most natural gradient step? Take the worst schedule (the one on average farthest away from the goal) and add an agent to the shift if the common is above your goal, or take away an agent if the common is below the goal. If you happen to are stuck where all shift changes make the schedule worse however the SLA shouldn’t be near the goal, return to a previously good schedule and begin from there again. I attempted other methods of picking a next schedule but this was essentially the most consistent. How do you realize when to stop? Set a tolerance to say that if every shift is on average lower than 30 seconds (for instance) from the goal, consider this solved. Stop if too many steps have been taken, and pick the very best schedule from those simulated.
In a really perfect world we’d run this program without end, trying each schedule until we discover the very best, but we would have liked this program to choose a schedule in under 5 min. Probably the most interesting a part of this project was testing optimization algorithms to search out the one which can get a very good solution the quickest.
The answer with the very best results used the very fact that you could control the variety of times you run a simulation, the trade off being that the less times you run a simulation, the less confident you might be in its results. Here’s what I discovered to work:
Start off with fewer simulations when starting to run the optimization algorithm, and because the objective approaches, increase the number. This works because to start with many shift changes will put the schedule within the direction of the target, so it’s not as essential to be as confident within the direction you selected. As a very good solution inches closer, it matters more where the schedule goes. Mix this with layered runs all ranging from the identical null point to make sure a wider breadth of explored solutions.
What’s great about this approach is that I’m in a position to control how long the algorithm runs. I can see how long simulation runs take, and adjust the variety of iterations accordingly, I may pick how over and over I begin from the starting spot. Every machine you run this algorithm on will operate a bit in another way, and consistent algorithm run time is of paramount importance to make sure that this project is helpful in the actual world.
#schedule is {(start_time, end_time): num_agents}{'Schedule':
{(0, 540): 5,
(30, 570): 2,
(60, 600): 1,
(90, 630): 1,
(120, 660): 4,
(150, 690): 1,
(180, 720): 0,
(210, 750): 1,
(240, 780): 18,
(0, 660): 0,
(30, 690): 0,
(60, 720): 0,
(90, 750): 0,
(120, 780): 3}
'Average Wait': 0.5738814985851588
'Employee Units': 660.0
'Worst Shift Time In Queue, Relative to Goal': 0.5965600329117189}