LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
MAPQN2RENV.m
1function envModel = MAPQN2RENV(model, options)
2% ENVMODEL = MAPQN2RENV(MODEL, OPTIONS)
3% Transform a queueing network with MMPP service into a random environment model
4%
5% This function transforms a queueing network where servers use MMPP2
6% (2-phase Markov Modulated Poisson Process) service distributions into
7% a random environment model with exponential services. The environment
8% has two stages (one per MMPP phase) with transitions defined by the
9% MMPP D0 matrix.
10%
11% Input:
12% model - Network with MMPP2 service distributions
13% options - Optional struct with configuration (reserved for future use)
14%
15% Output:
16% envModel - Env model with exponential services modulated by MMPP phases
17%
18% The transformation works as follows:
19% - Input MMPP2 has D0 (phase transitions) and D1 (diagonal service rates)
20% - Output has 2 environment stages with exponential services
21% - Stage 0 uses service rate λ₀ = D1(1,1)
22% - Stage 1 uses service rate λ₁ = D1(2,2)
23% - Environment transitions: σ₀₁ = D0(1,2), σ₁₀ = D0(2,1)
24%
25% Example:
26% model = Network('MMPP_Queue');
27% source = Source(model, 'Source');
28% queue = Queue(model, 'Queue', SchedStrategy.FCFS);
29% sink = Sink(model, 'Sink');
30% jobclass = OpenClass(model, 'Class1');
31% source.setArrival(jobclass, Exp(1.0));
32% queue.setService(jobclass, MMPP2(2.0, 3.0, 0.5, 0.5));
33% model.link(Network.serialRouting(source, queue, sink));
34% envModel = MAPQN2RENV(model);
35
36 % Input validation
37 if ~isa(model, 'Network')
38 error('MAPQN2RENV:InvalidInput', 'First argument must be a Network object');
39 end
40
41 % Phase 1: Input Validation
42 % Verify all service distributions are MMPP2 and D1 is diagonal
43 [mmpp2Found, mmppParams] = validateAndExtractMMPP(model);
44
45 if ~mmpp2Found
46 error('MAPQN2RENV:NoMMPP', 'Network must contain at least one MMPP2 service distribution');
47 end
48
49 % Phase 2: Extract MMPP Parameters
50 D0 = mmppParams.D0;
51 D1 = mmppParams.D1;
52
53 % Extract service rates from D1 diagonal
54 lambda0 = D1(1, 1);
55 lambda1 = D1(2, 2);
56
57 % Extract transition rates from D0 off-diagonal
58 sigma01 = D0(1, 2);
59 sigma10 = D0(2, 1);
60
61 % Validate rates are non-negative
62 if lambda0 < 0 || lambda1 < 0 || sigma01 < 0 || sigma10 < 0
63 error('MAPQN2RENV:InvalidRates', 'All extracted rates must be non-negative');
64 end
65
66 % Phase 3: Create Environment Model
67 envModel = Env('MAPQN_Env');
68
69 % Phase 4: Build Stage Networks
70 % Create stage network for Phase 0
71 stageNet0 = buildStageNetwork(model, 'Phase0', lambda0);
72 envModel.addStage('Phase0', 'item', stageNet0);
73
74 % Create stage network for Phase 1
75 stageNet1 = buildStageNetwork(model, 'Phase1', lambda1);
76 envModel.addStage('Phase1', 'item', stageNet1);
77
78 % Phase 5: Add Environment Transitions
79 if sigma01 > 0
80 envModel.addTransition('Phase0', 'Phase1', Exp(sigma01));
81 end
82
83 if sigma10 > 0
84 envModel.addTransition('Phase1', 'Phase0', Exp(sigma10));
85 end
86
87 % Initialize environment
88 envModel.init();
89end
90
91%% Helper: Validate MMPP2 and Extract Parameters
92function [found, params] = validateAndExtractMMPP(model)
93 found = false;
94 params = struct();
95
96 nodes = model.nodes;
97 firstMMPP2 = [];
98
99 % Iterate through all nodes to find MMPP2 distributions
100 for i = 1:length(nodes)
101 node = nodes{i};
102
103 % Check if node is a Queue or Delay
104 if ~(isa(node, 'Queue') || isa(node, 'Delay'))
105 continue;
106 end
107
108 % Check service distributions for all classes
109 if isa(node, 'Queue')
110 for r = 1:length(node.server.serviceProcess)
111 if ~isempty(node.server.serviceProcess{r})
112 dist = node.server.serviceProcess{r}{end};
113
114 if isa(dist, 'MMPP2')
115 if isempty(firstMMPP2)
116 firstMMPP2 = dist;
117 found = true;
118 else
119 % Verify consistency with first MMPP2
120 verifyMMPPConsistency(firstMMPP2, dist);
121 end
122 elseif isa(dist, 'MAP') && ~isa(dist, 'MMPP2')
123 % Reject generic MAP
124 D0_check = dist.D(0);
125 D1_check = dist.D(1);
126
127 % Check if D1 is diagonal
128 for ii = 1:size(D1_check, 1)
129 for jj = 1:size(D1_check, 2)
130 if ii ~= jj && abs(D1_check(ii, jj)) > GlobalConstants.Zero
131 line_warning(mfilename, 'Generic MAP detected. Only MMPP (diagonal D1) is supported. Aborting transformation.');
132 error('MAPQN2RENV:GenericMAP', 'Generic MAP with non-diagonal D1 is not supported. Use MMPP2 instead.');
133 end
134 end
135 end
136 end
137 end
138 end
139 end
140 end
141
142 if found && ~isempty(firstMMPP2)
143 D0 = firstMMPP2.D(0);
144 D1 = firstMMPP2.D(1);
145
146 params.D0 = D0;
147 params.D1 = D1;
148 end
149end
150
151%% Helper: Verify MMPP Consistency
152function verifyMMPPConsistency(mmpp1, mmpp2)
153 % Future: Could add checks for consistent MMPP parameters across network
154 % For now, just allow multiple MMPP2 with same parameters
155end
156
157%% Helper: Build Stage Network
158function stageNet = buildStageNetwork(originalModel, stageName, expRate)
159 % Clone the network structure and replace MMPP2 with Exp
160 stageNet = Network([originalModel.name '_' stageName]);
161
162 % Build mapping from original nodes to cloned nodes
163 nodeMap = containers.Map('KeyType', 'char', 'ValueType', 'any');
164 origClasses = originalModel.getClasses();
165
166 % PASS 1: Create all nodes first (Source, Sink, Delay, Queue)
167 % All nodes must exist before creating job classes
168 for i = 1:length(originalModel.nodes)
169 origNode = originalModel.nodes{i};
170 if isa(origNode, 'Source')
171 newNode = Source(stageNet, origNode.name);
172 nodeMap(origNode.name) = newNode;
173 elseif isa(origNode, 'Sink')
174 newNode = Sink(stageNet, origNode.name);
175 nodeMap(origNode.name) = newNode;
176 elseif isa(origNode, 'Delay')
177 newNode = Delay(stageNet, origNode.name);
178 nodeMap(origNode.name) = newNode;
179 elseif isa(origNode, 'Queue')
180 newNode = Queue(stageNet, origNode.name, origNode.schedStrategy);
181 % Copy number of servers and capacity
182 if hasProperty(origNode, 'numberOfServers')
183 newNode.setNumberOfServers(origNode.numberOfServers);
184 end
185 if hasProperty(origNode, 'cap')
186 newNode.setCapacity(origNode.cap);
187 end
188 nodeMap(origNode.name) = newNode;
189 end
190 end
191
192 % PASS 2: Create all job classes (all nodes must exist first)
193 for r = 1:length(origClasses)
194 origClass = origClasses{r};
195 if isa(origClass, 'OpenClass')
196 OpenClass(stageNet, origClass.name);
197 elseif isa(origClass, 'ClosedClass')
198 % Find reference station in the new network
199 origRefStat = origClass.refstat;
200 if isKey(nodeMap, origRefStat.name)
201 newRefStat = nodeMap(origRefStat.name);
202 else
203 % Fallback to first node if reference station not yet created
204 newRefStat = stageNet.nodes{1};
205 end
206 ClosedClass(stageNet, origClass.name, origClass.population, newRefStat);
207 end
208 end
209
210 % PASS 4: Set arrivals from Source nodes
211 for i = 1:length(originalModel.nodes)
212 origNode = originalModel.nodes{i};
213
214 if isa(origNode, 'Source') && isKey(nodeMap, origNode.name)
215 newNode = nodeMap(origNode.name);
216
217 % Copy arrival distributions
218 origInput = origNode.input;
219 if ~isempty(origInput) && ~isempty(origInput.sourceClasses)
220 for r = 1:length(origInput.sourceClasses)
221 if ~isempty(origInput.sourceClasses{r})
222 origDist = origInput.sourceClasses{r}{end};
223 classes = stageNet.getClasses();
224 if r <= length(classes)
225 jobClass = classes{r};
226 newNode.setArrival(jobClass, origDist);
227 end
228 end
229 end
230 end
231 end
232 end
233
234 % PASS 5: Set services on Queue/Delay nodes, replacing MMPP2 with Exp
235 for i = 1:length(originalModel.nodes)
236 origNode = originalModel.nodes{i};
237
238 if (isa(origNode, 'Queue') || isa(origNode, 'Delay')) && isKey(nodeMap, origNode.name)
239 newNode = nodeMap(origNode.name);
240 classes = stageNet.getClasses();
241
242 % Copy service distributions, replacing MMPP2 with Exp
243 if isa(origNode, 'Queue') && hasProperty(origNode, 'server')
244 for r = 1:length(origNode.server.serviceProcess)
245 if ~isempty(origNode.server.serviceProcess{r})
246 origDist = origNode.server.serviceProcess{r}{end};
247
248 if isa(origDist, 'MMPP2')
249 % Replace with exponential
250 newDist = Exp(expRate);
251 else
252 % Keep other distributions as-is
253 newDist = origDist;
254 end
255
256 % Set service for this class
257 if r <= length(classes)
258 jobClass = classes{r};
259 newNode.setService(jobClass, newDist);
260 end
261 end
262 end
263 elseif isa(origNode, 'Delay') && hasProperty(origNode, 'server')
264 % Handle Delay node services
265 for r = 1:length(origNode.server.serviceProcess)
266 if ~isempty(origNode.server.serviceProcess{r})
267 origDist = origNode.server.serviceProcess{r}{end};
268
269 if isa(origDist, 'MMPP2')
270 % Replace with exponential
271 newDist = Exp(expRate);
272 else
273 % Keep other distributions as-is
274 newDist = origDist;
275 end
276
277 % Set service for this class
278 if r <= length(classes)
279 jobClass = classes{r};
280 newNode.setService(jobClass, newDist);
281 end
282 end
283 end
284 end
285 end
286 end
287
288 % PASS 6: Setup routing from original model
289 % Determine if this is an open or closed network
290 isClosedNetwork = any(cellfun(@(c) isa(c, 'ClosedClass'), origClasses));
291
292 % Collect route nodes in order
293 routeNodes = {};
294 for i = 1:length(originalModel.nodes)
295 origNode = originalModel.nodes{i};
296 if isKey(nodeMap, origNode.name)
297 routeNodes{end+1} = nodeMap(origNode.name);
298 end
299 end
300
301 % Setup routing based on network type
302 if isClosedNetwork
303 % Closed network: use cyclic routing
304 if length(routeNodes) >= 2
305 try
306 stageNet.link(Network.serialRouting(routeNodes{:}));
307 catch
308 % If serial routing fails, create cyclic routing manually
309 for i = 1:length(routeNodes)
310 fromNode = routeNodes{i};
311 toNode = routeNodes{mod(i, length(routeNodes)) + 1};
312 try
313 P = stageNet.initRoutingMatrix();
314 for r = 1:length(stageNet.getClasses())
315 P{r, r}(stageNet.getNodeIndex(fromNode), stageNet.getNodeIndex(toNode)) = 1.0;
316 end
317 stageNet.link(P);
318 catch
319 % Skip if fails
320 end
321 end
322 end
323 end
324 else
325 % Open network: use serial routing
326 if length(routeNodes) > 2
327 try
328 stageNet.link(Network.serialRouting(routeNodes{:}));
329 catch
330 % If serial routing fails, try manual node-to-node routing
331 for i = 1:length(routeNodes)-1
332 fromNode = routeNodes{i};
333 toNode = routeNodes{i+1};
334 try
335 stageNet.setRouting(fromNode, toNode, 1.0);
336 catch
337 % Skip if fails
338 end
339 end
340 end
341 elseif length(routeNodes) == 2
342 try
343 stageNet.link(Network.serialRouting(routeNodes{1}, routeNodes{2}));
344 catch
345 % Skip routing if fails
346 end
347 end
348 end
349end
350
351%% Helper: Check if object has property
352function hasIt = hasProperty(obj, propName)
353 try
354 dummy = obj.(propName); %#ok<NASGU>
355 hasIt = true;
356 catch
357 hasIt = false;
358 end
359end
Definition mmt.m:92