/* Queue.mm */ /* This is a reprogramming of Tom Grossman's N-server queue simulation - in MM. It has two main components: a set of customers, and a set of servers. The number of customers and servers is fixed before starting. The scenario is that customers enter a shop, go to a server, may queue, get served, and leave. Each customer interacts once only and then leaves. We could slice up time by allocating equally-spaced time points to successive rows. Instead, following Grossman's simulation, each row represents one customer's complete sequence of transactions: ariving, starting to queue, starting being served, and leaving. We handle this in MM by declaring our base to be 'event = 1..10' or whatever, where each point represents a separate customer. This being done, we do not need to represent each customer as a distinct object (though from a sheaf semantics point of view, I would prefer to start by doing so, and then transform the program into one where they're all collapsed into one object). Instead, we define one 'customers' object to represent the whole lot. This has attributes such as customer_number, arrival_time, service_start_time and service_duration, all quantified over 'event'. Each customer has a unique customer number: this is 1 for the first customer, and is incremented for each successive customer. Having defined a basic 'customers' object, we can then extend it in various ways. One thing we shall want to do is to generate the arrival times. Since we may want to change the distribution function, we shall have various sub-objects which inherit from 'customers'. Now we need to do the servers. Grossman allocates one column to each server, so we shall do likewise. This would make it easy to treat each server as a distinct object. But again, there's not much to be gained by doing so - we might as well treat the whole group as one object. (This is partly because I've not decided how best to split up Grossman's code across servers if we do make them separate objects.) Finally, we define some specialisations of 'customers' with particular distributions of arrival times, and then specify the worksheet to be built. */ /* Declare our units. */ unit duration // Durations, as minutes. unit time = @duration // Points in time, as hh:mm. unit customer_id ~ integer // Customer numbers, from 1 upwards. unit server_id ~ integer // Server numbers, from 1 upwards. /* Define our 'base'. This is the interval over which customer numbers will range, to be allocated to rows. */ base event = 1 .. 10 /* Define a succession of customers. */ customers = <customer_number[event] as customer_id arrival_time[event] as time service_start_time[event] as time service_duration[event] as duration > where (e) customer_number[e]=e // // The above represents one sequence of customers. Each // attribute will go into a column. We have four attributes: // customer number, arrival time, service start time, and // service duration. // // Each attribute has as many rows as there are points // in 'event'. The equation stipulates that the customer // number in any row e is equal to e. /* Define a group of N servers. */ servers( N:server_id ) = <start as time customer_number[event] as customer_id arrival_time[event] as time service_start_time[event] as time service_duration[event] as duration service_end_time[event] as time first_free[event] as server_id potential_start_time[event,N] as time > where (e>1,N) potential_start_time[e,N] = max( service_end_time[e-1], arrival_time[e] ) (N) potential_start_time[1,N] = start (e) service_start_time[e] = min( (N)potential_start_time[e] ) // The argument must deliver a range into min. (e) service_end_time[e] = service_start_time[e] + service_duration[e] (e) first_free[e] = match( actual_start_time[e], (N)potential_start_time[e], 0 ) // The 2nd argument must deliver a range into 'match'. // 'match' returns the relative position of the first cell in // its 2nd argument which is the same as the 1st argument. // The 0 in the 3rd argument, taken from Grossman's simulation, // should never happen, I think. // // This has similar attributes to the 'customers' object, plus // some others taken from Grossman's simulation. // // Attribute potential_start_time is a kind of two-dimensional array. // The first dimension is down the rows, as before. The second dimension // is along columns: one column for each value of N, i.e. for each // server number. This represents the time at which each server _could_ // start, given when it finishes the previous customer. This is the // greater of the current customer's arrival time, and the previous customer's // finishing time. // // service_start_time is the least value of potential_start_time over // all servers. // // service_end_time is this plus the service duration. That's not set // here - it will come from the customer. // // first_free is the number of the first free server, i.e. that with // the smallest potential_start_time. This is found by searching all // servers' potential_start_times for service_start_time, i.e. the // least value of potential_start_time. // // 'start' is the time at which the simulation begins. Notice that it // is not subscripted by 'event'. So it will occupy only one cell. It // will be set later. // // The equations use Excel's built-in max, min and match functions. // Where the arguments are unsubscripted, the compiler translates them // into entire ranges. /* Declare a sequence of customers with an additional 'interarrival_time' attribute and a start time. */ customers_with_interarrival_times = customers * <interarrival_time[event] as duration start as time > where (e>1) arrival_time[e] = arrival_time[e-1] + interarrival_time[e] & arrival_time = start + interarrival_time // // interarrival_time is the time between arrivals of successive // customers. This can be calculated in different ways for different // sub-objects of customers_with_interarrival_times. // // Notice that 'start' is not subscripted by 'event'. So it will // occupy only one cell. This is the start time for the entire // simulation, as for the N servers above. /* Declare a sequence of customers with random interarrival times by extending the previous definition. */ random_customers = customers_with_interarrival_times where (e) interarrival_time[e] = rand() // // We use Excel's built-in rand function to allocate a random // interarrival time to each point in 'event'. /* Finally, tell the compiler to build the worksheet. */ model ( random_customers on servers(4) ) where start=9. // // The 'on' operator makes attributes in 'random_customers' // equal to attributes of the same name in 'servers(4)'. So, // for example, it makes the two values of 'start' equal. The // equation sets these both to 9.