1function result = qsys_mmck(lambda, mu, c, K)
2% QSYS_MMCK Exact closed-form analysis of an M/M/c/K queue.
4% RESULT = QSYS_MMCK(LAMBDA, MU, C, K) analyzes an M/M/c/K queue with:
5% LAMBDA - Poisson arrival rate (positive scalar)
6% MU - Exponential service rate per server (positive scalar)
7% C - Number of servers (positive integer)
8% K - System capacity, total jobs allowed (integer, K >= C)
10% Stationary distribution (Erlang-B/C truncated form):
11% a = lambda/mu, rho = a/c
12% p_n = (a^n / n!) * p_0 for 0 <= n <= c
13% p_n = (a^c / c!) * rho^(n-c) * p_0 for c <= n <= K
14% p_0 = 1 / [ sum_{n=0}^{c-1} a^n/n! + (a^c/c!) * sum_{n=0}^{K-c} rho^n ]
16% Returns a
struct with fields:
17% meanQueueLength - Mean number of jobs in system, L
18% meanQueueLengthQ - Mean number waiting in queue, Lq
19% meanWaitingTime - Mean waiting time in queue, Wq (Little)
20% meanSojournTime - Mean sojourn time, W = Wq + 1/mu
21% utilization - Per-server utilization, lambda_eff/(c*mu)
22% throughput - Effective throughput, lambda*(1 - p_K)
23% lossProbability - Blocking probability, p_K
24% queueLengthDist - Distribution as 1x(K+1) row: [p_0, p_1, ..., p_K]
25% analyzer - Identifier string
28% r = qsys_mmck(1, 1, 2, 4); % M/M/2/4 with rho = 0.5
29% r.meanQueueLength % 1.1304
30% r.lossProbability % 0.0435
32% See also QSYS_MM1K_LOSS, QSYS_MMK, QSYS_MAPDC
34% Copyright (c) 2012-2026, Imperial College London
38if ~isnumeric(lambda) || ~isscalar(lambda) || ~isreal(lambda) || lambda <= 0
39 line_error(mfilename, 'lambda must be a positive real scalar');
41if ~isnumeric(mu) || ~isscalar(mu) || ~isreal(mu) || mu <= 0
42 line_error(mfilename, 'mu must be a positive real scalar');
44if ~isnumeric(c) || ~isscalar(c) || ~isreal(c) || c < 1 || floor(c) ~= c
45 line_error(mfilename, 'c must be a positive integer');
47if ~isnumeric(K) || ~isscalar(K) || ~isreal(K) || K < c || floor(K) ~= K
48 line_error(mfilename, 'K must be an integer >= c');
54% Build unnormalized stationary distribution
56% Levels 0..c-1: Erlang-B partial sum terms
58 p(n+1) = a^n / factorial(n);
60% Levels c..K: geometric tail in rho beyond level c
61ac_over_cfact = a^c / factorial(c);
63 p(n+1) = ac_over_cfact * rho^(n-c);
67if ~isfinite(S) || S <= 0
68 line_error(mfilename, 'Stationary distribution failed to normalize (numerical overflow?)');
74L = levels * p(:); % Mean # in system
75p_K = p(K+1); % Blocking probability
76lambdaEff = lambda * (1 - p_K); % Effective throughput
77n_waiting = max(0, levels - c); % Jobs waiting at each level
78Lq = n_waiting * p(:); % Mean # in queue
79util = lambdaEff / (c * mu); % Per-server utilization
89result.meanQueueLength = L;
90result.meanQueueLengthQ = Lq;
91result.meanWaitingTime = Wq;
92result.meanSojournTime = W;
93result.utilization = util;
94result.throughput = lambdaEff;
95result.lossProbability = p_K;
96result.queueLengthDist = p;
97result.analyzer = sprintf('exact:M/M/%d/%d', c, K);