LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
BMAP.m
1classdef BMAP < MarkedMAP
2 % Batch Markovian Arrival Process (BMAP)
3 %
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
9 % - ...
10 % - Dk: rate matrix for transitions generating k arrivals
11 %
12 % BMAP extends MarkedMAP where each "mark" k represents a batch size k.
13 %
14 % Copyright (c) 2012-2026, Imperial College London
15 % All rights reserved.
16
17 methods
18 % Constructor
19 function self = BMAP(D)
20 % SELF = BMAP(D)
21 %
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)
25 %
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)
29
30 if nargin == 0
31 D = {};
32 end
33
34 % see _kb/04-networkstruct.md (BMAP.m marking-count convention)
35
36 if isempty(D)
37 K = 0;
38 else
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
43
44 % Compute total arrival matrix
45 D1_total = D{2};
46 for k = 3:length(D)
47 D1_total = D1_total + D{k};
48 end
49
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
54 for k = 1:K
55 D_mmap{2+k} = D{1+k}; % Dk (batch size k)
56 end
57
58 D = D_mmap;
59 end
60
61 % Call parent MarkedMAP constructor
62 self@MarkedMAP(D, K);
63
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
67 self.process = D;
68
69 % Validate generator
70 self.validateGenerator();
71 end
72
73 % Validate that D0 + sum(Dk) forms a proper infinitesimal generator
74 function validateGenerator(self)
75 D0 = self.D(0);
76 nPhases = length(D0);
77
78 % Compute D0 + sum(Dk)
79 generator = D0;
80 for k = 1:self.getMaxBatchSize()
81 generator = generator + self.D(1, k); % D(1, k) returns process{2+k} = Dk
82 end
83
84 % Check row sums (should be close to zero)
85 rowSums = sum(generator, 2);
86 maxRowSum = max(abs(rowSums));
87
88 if maxRowSum > 1e-6
89 warning('BMAP:InvalidGenerator', ...
90 'BMAP generator row sums are not zero (max: %.2e). Consider calling normalize().', maxRowSum);
91 end
92 end
93
94 % Get maximum batch size
95 function k = getMaxBatchSize(self)
96 % K = GETMAXBATCHSIZE()
97 %
98 % Returns the maximum batch size k where Dk is defined
99 k = self.getNumberOfTypes();
100 end
101
102 % Get mean batch size
103 function mean_bs = getMeanBatchSize(self)
104 % MEAN_BS = GETMEANBATCHSIZE()
105 %
106 % Computes the mean batch size as a weighted average:
107 % E[batch size] = sum(k * rate_k) / sum(rate_k)
108
109 % Get stationary distribution using D0 and D1_total
110 D0 = self.D(0);
111 D1_total = self.D(1);
112 pi = map_pie({D0, D1_total});
113
114 % Compute weighted average
115 totalRate = 0;
116 weightedSum = 0;
117 maxBatch = self.getMaxBatchSize();
118
119 for k = 1:maxBatch
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;
124 end
125
126 if totalRate > 0
127 mean_bs = weightedSum / totalRate;
128 else
129 mean_bs = 0;
130 end
131 end
132
133 % Get batch rates
134 function rates = getBatchRates(self)
135 % RATES = GETBATCHRATES()
136 %
137 % Returns array where rates(k) is the rate of batch size k arrivals
138
139 D0 = self.D(0);
140 D1_total = self.D(1);
141 maxBatch = self.getMaxBatchSize();
142
143 % Get stationary distribution
144 pi = map_pie({D0, D1_total});
145
146 % Compute rate for each batch size
147 rates = zeros(1, maxBatch);
148 for k = 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));
151 end
152 end
153
154 % Override toString-like display
155 function display(self)
156 % DISPLAY(SELF)
157 fprintf('BMAP(phases=%d, maxBatchSize=%d)\n', ...
158 self.getNumberOfPhases(), self.getMaxBatchSize());
159 end
160
161 % Get distribution name
162 function name = getName(self)
163 % NAME = GETNAME()
164 %
165 % Returns the name of this distribution type
166 name = 'BMAP';
167 end
168
169 % Sample from BMAP (returns inter-arrival times and batch sizes)
170 function [X, B] = sample(self, n)
171 % [X, B] = SAMPLE(N)
172 %
173 % Sample n batches from the BMAP
174 % Returns:
175 % X: inter-arrival times (between batches)
176 % B: batch sizes for each arrival
177
178 if nargin < 2
179 n = 1;
180 end
181
182 % Use parent MarkedMAP sampling
183 % This returns inter-arrival times and class labels
184 [X, C] = sample@MarkedMAP(self, n);
185
186 % Convert class labels to batch sizes
187 % Class k corresponds to batch size k
188 B = C;
189 end
190
191 % Sample only inter-batch times
192 function X = sampleInterBatch(self, n)
193 % X = SAMPLEINTERBATCH(N)
194 %
195 % Sample n inter-batch arrival times
196 % (ignoring batch sizes)
197
198 if nargin < 2
199 n = 1;
200 end
201
202 % Sample from aggregate MAP
203 map = self.toMAP();
204 X = map.sample(n);
205 end
206
207 % Get inter-batch MAP
208 function map = getInterBatchMAP(self)
209 % MAP = GETINTERBATCHMAP()
210 %
211 % Returns the underlying MAP for inter-batch arrivals
212 map = self.toMAP();
213 end
214 end
215
216 methods (Static)
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)
220 %
221 % Create BMAP from a base MAP and batch size distribution
222 %
223 % Inputs:
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)
228 %
229 % Output:
230 % bmap: BMAP constructed from the base MAP and batch distribution
231
232 if length(batchSizes) ~= length(pmf)
233 line_error(mfilename, 'Batch sizes and PMF must have the same length');
234 end
235
236 % Normalize PMF
237 pmf = pmf / sum(pmf);
238
239 % Find maximum batch size
240 maxBatch = max(batchSizes);
241
242 % Create D matrices: Dk = D1 * pmf(batchSize==k)
243 D = cell(1, maxBatch + 1);
244 D{1} = D0; % D0
245
246 % Initialize Dk matrices to zero
247 for k = 1:maxBatch
248 D{1+k} = zeros(size(D0));
249 end
250
251 % Set Dk based on batch sizes and PMF
252 for i = 1:length(batchSizes)
253 k = batchSizes(i);
254 if k < 1 || k > maxBatch
255 line_error(mfilename, sprintf('Invalid batch size: %d', k));
256 end
257 D{1+k} = D{1+k} + D1 * pmf(i);
258 end
259
260 % Create BMAP
261 bmap = BMAP(D);
262 end
263
264 % Generate random BMAP
265 function bmap = rand(order, maxBatchSize)
266 % BMAP = RAND(ORDER, MAXBATCHSIZE)
267 %
268 % Generate random BMAP using uniform random numbers
269 %
270 % Inputs:
271 % order: number of phases (default: 2)
272 % maxBatchSize: maximum batch size (default: 3)
273
274 if nargin < 1
275 order = 2;
276 end
277 if nargin < 2
278 maxBatchSize = 3;
279 end
280
281 % Generate random MarkedMAP and interpret as BMAP
282 mmap = MarkedMAP.rand(order, maxBatchSize);
283
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
289 end
290
291 bmap = BMAP(D);
292 end
293 end
294end