1classdef BMAP < MarkedMAP
2 % Batch Markovian Arrival Process (BMAP)
4 % BMAP
is a point process where arrivals occur in batches.
5 % Uses the standard BMAP representation:
6 % - D0: infinitesimal generator
for transitions without arrivals
7 % - D1: rate matrix
for transitions generating 1 arrival
8 % - D2: rate matrix
for transitions generating 2 arrivals
10 % - Dk: rate matrix
for transitions generating k arrivals
12 % BMAP extends MarkedMAP where each
"mark" k represents a batch size k.
14 % Copyright (c) 2012-2026, Imperial College London
15 % All rights reserved.
19 function self = BMAP(D)
22 % D
is a cell array {D0, D1, D2, ..., Dk} where:
23 % - D0: transitions without arrivals
24 % - Dk: transitions generating k arrivals (
for k >= 1)
26 % The number of marking types K equals the maximum batch size
27 % (i.e., K = length(D) - 2 when D also includes D1_total,
28 % or K = length(D) - 1 otherwise)
34 % see _kb/04-networkstruct.md (BMAP.m marking-count convention)
39 % Assume format {D0, D1, D2, ..., Dk} where Dk generates k arrivals
40 % Need to convert to MarkedMAP format {D0, D1_total, D1, D2, ..., Dk}
41 % where D1_total = sum(D1, D2, ..., Dk)
42 K = length(D) - 1; % max batch size
44 % Compute total arrival matrix
47 D1_total = D1_total + D{k};
50 % Create MarkedMAP cell array
51 D_mmap = cell(1, K+2);
52 D_mmap{1} = D{1}; % D0
53 D_mmap{2} = D1_total; % D1_total
55 D_mmap{2+k} = D{1+k}; % Dk (batch size k)
61 % Call parent MarkedMAP constructor
64 % Fix self.process which
is incorrectly set by MarkedMAP constructor
65 % MarkedMAP constructor has a bug on line 33-37 where it creates
66 % a cell array of size K-1 instead of K+2
70 self.validateGenerator();
73 % Validate that D0 + sum(Dk) forms a proper infinitesimal generator
74 function validateGenerator(self)
78 % Compute D0 + sum(Dk)
80 for k = 1:self.getMaxBatchSize()
81 generator = generator + self.D(1, k); % D(1, k) returns process{2+k} = Dk
84 % Check row sums (should be close to zero)
85 rowSums = sum(generator, 2);
86 maxRowSum = max(abs(rowSums));
89 warning(
'BMAP:InvalidGenerator', ...
90 'BMAP generator row sums are not zero (max: %.2e). Consider calling normalize().', maxRowSum);
94 % Get maximum batch size
95 function k = getMaxBatchSize(self)
96 % K = GETMAXBATCHSIZE()
98 % Returns the maximum batch size k where Dk
is defined
99 k = self.getNumberOfTypes();
102 % Get mean batch size
103 function mean_bs = getMeanBatchSize(self)
104 % MEAN_BS = GETMEANBATCHSIZE()
106 % Computes the mean batch size as a weighted average:
107 % E[batch size] = sum(k * rate_k) / sum(rate_k)
109 % Get stationary distribution
using D0 and D1_total
111 D1_total = self.D(1);
112 pi = map_pie({D0, D1_total});
114 % Compute weighted average
117 maxBatch = self.getMaxBatchSize();
120 Dk = self.D(1, k); % D(1, k) returns process{1+1+k} = process{2+k} = Dk
121 rate_k = sum(pi * Dk * ones(size(Dk, 1), 1));
122 totalRate = totalRate + rate_k;
123 weightedSum = weightedSum + k * rate_k;
127 mean_bs = weightedSum / totalRate;
134 function rates = getBatchRates(self)
135 % RATES = GETBATCHRATES()
137 % Returns array where rates(k)
is the rate of batch size k arrivals
140 D1_total = self.D(1);
141 maxBatch = self.getMaxBatchSize();
143 % Get stationary distribution
144 pi = map_pie({D0, D1_total});
146 % Compute rate
for each batch size
147 rates = zeros(1, maxBatch);
149 Dk = self.D(1, k); % D(1, k) returns process{1+1+k} = process{2+k} = Dk
150 rates(k) = sum(pi * Dk * ones(size(Dk, 1), 1));
154 % Override toString-like display
155 function display(self)
157 fprintf(
'BMAP(phases=%d, maxBatchSize=%d)\n', ...
158 self.getNumberOfPhases(), self.getMaxBatchSize());
161 % Get distribution name
162 function name = getName(self)
165 % Returns the name of
this distribution type
169 % Sample from BMAP (returns inter-arrival times and batch sizes)
170 function [X, B] = sample(self, n)
173 % Sample n batches from the BMAP
175 % X: inter-arrival times (between batches)
176 % B: batch sizes
for each arrival
182 % Use parent MarkedMAP sampling
183 % This returns inter-arrival times and
class labels
184 [X, C] = sample@MarkedMAP(self, n);
186 % Convert
class labels to batch sizes
187 % Class k corresponds to batch size k
191 % Sample only inter-batch times
192 function X = sampleInterBatch(self, n)
193 % X = SAMPLEINTERBATCH(N)
195 % Sample n inter-batch arrival times
196 % (ignoring batch sizes)
202 % Sample from aggregate MAP
207 % Get inter-batch MAP
208 function
map = getInterBatchMAP(self)
209 % MAP = GETINTERBATCHMAP()
211 % Returns the underlying MAP
for inter-batch arrivals
217 % Factory method: create BMAP from MAP + batch size distribution
218 function bmap = fromMAPWithBatchPMF(D0, D1, batchSizes, pmf)
219 % BMAP = FROMMAPWITHBATCHPMF(D0, D1, BATCHSIZES, PMF)
221 % Create BMAP from a base MAP and batch size distribution
224 % D0: base MAP
's D0 matrix (transitions without batch arrivals)
225 % D1: base MAP's D1 matrix (inter-batch arrival transitions)
226 % batchSizes: array of batch sizes (e.g., [1, 2, 4, 8])
227 % pmf: probability mass function
for batch sizes (must sum to 1)
230 % bmap: BMAP constructed from the base MAP and batch distribution
232 if length(batchSizes) ~= length(pmf)
233 line_error(mfilename,
'Batch sizes and PMF must have the same length');
237 pmf = pmf / sum(pmf);
239 % Find maximum batch size
240 maxBatch = max(batchSizes);
242 % Create D matrices: Dk = D1 * pmf(batchSize==k)
243 D = cell(1, maxBatch + 1);
246 % Initialize Dk matrices to zero
248 D{1+k} = zeros(size(D0));
251 % Set Dk based on batch sizes and PMF
252 for i = 1:length(batchSizes)
254 if k < 1 || k > maxBatch
255 line_error(mfilename, sprintf(
'Invalid batch size: %d', k));
257 D{1+k} = D{1+k} + D1 * pmf(i);
264 % Generate random BMAP
265 function bmap = rand(order, maxBatchSize)
266 % BMAP = RAND(ORDER, MAXBATCHSIZE)
268 % Generate random BMAP
using uniform random numbers
271 % order: number of phases (
default: 2)
272 % maxBatchSize: maximum batch size (default: 3)
281 % Generate random MarkedMAP and interpret as BMAP
282 mmap = MarkedMAP.rand(order, maxBatchSize);
284 % Convert to BMAP format
285 D = cell(1, maxBatchSize + 1);
286 D{1} = mmap.D(0); % D0
287 for k = 1:maxBatchSize
288 D{1+k} = mmap.D(1, k-1); % Dk