LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
NetworkGenerator.m
1classdef NetworkGenerator < handle
2 % A generator object that generates queueing network models
3 % based on user specification. Characteristics of generated
4 % models can be configured via the generator's properties. See
5 % user guide in report for detailed usage instructions.
6
7 properties
8 schedStrat char
9 routingStrat char
10 distribution char
11 cclassJobLoad char
12 hasVaryingServiceRates (1, 1) logical
13 hasMultiServerQueues (1, 1) logical
14 hasRandomCSNodes (1, 1) logical
15 hasMultiChainCS (1, 1) logical
16 topologyFcn function_handle
17 end
18
19 properties (SetAccess = private)
20 maxServers = 40
21 highJobLoadRange = [31 40]
22 medJobLoadRange = [11 20]
23 lowJobLoadRange = [1 5]
24 end
25
26 methods
27 function obj = NetworkGenerator(varargin)
28 p = inputParser;
29 addParameter(p, 'schedStrat', 'randomize'); % alternatives: 'fcfs', 'ps'
30 addParameter(p, 'routingStrat', 'randomize'); % alternatives: 'Random', 'Probabilities'
31 addParameter(p, 'distribution', 'randomize'); % alternatives: 'exp', 'erlang', 'hyperexp'
32 addParameter(p, 'cclassJobLoad', 'randomize'); % alternatives: 'low', 'medium', 'high'
33 addParameter(p, 'hasVaryingServiceRates', true);
34 addParameter(p, 'hasMultiServerQueues', true);
35 addParameter(p, 'hasRandomCSNodes', true);
36 addParameter(p, 'hasMultiChainCS', true);
37 addParameter(p, 'topologyFcn', @NetworkGenerator.randGraph);
38 parse(p, varargin{:});
39
40 obj.schedStrat = p.Results.schedStrat;
41 obj.routingStrat = p.Results.routingStrat;
42 obj.distribution = p.Results.distribution;
43 obj.cclassJobLoad = p.Results.cclassJobLoad;
44 obj.hasVaryingServiceRates = p.Results.hasVaryingServiceRates;
45 obj.hasMultiServerQueues = p.Results.hasMultiServerQueues;
46 obj.hasRandomCSNodes = p.Results.hasRandomCSNodes;
47 obj.hasMultiChainCS = p.Results.hasMultiChainCS;
48 obj.topologyFcn = p.Results.topologyFcn;
49 end
50
51 %% Main function to call.
52 % Returns a generated QN model according to
53 % specified properties of the NetworkGenerator object
54 function model = generate(obj, numQueues, numDelays, numOClass, numCClass)
55 if nargin < 2 || isempty(numQueues)
56 numQueues = randi([1, 8]);
57 end
58 if nargin < 3 || isempty(numDelays)
59 if numQueues > 1
60 numDelays = randi([0,1]);
61 else
62 numDelays = 1;
63 end
64 end
65 if nargin < 4 || isempty(numOClass)
66 numOClass = 0;
67 end
68 if nargin < 5 || isempty(numCClass)
69 numCClass = randi([1, 4]);
70 end
71 obj.validateArgs(numQueues, numDelays, numOClass, numCClass);
72 model = Network('nw');
73 stations = obj.createStations(model, numQueues, numDelays, numOClass);
74 classes = obj.createClasses(model, numOClass, numCClass);
75 obj.setServiceProcesses(stations, classes);
76 obj.defineTopology(model, obj.topologyFcn(length(stations)));
77 %model = NetworkGenerator.initDefaultCustom(model);
78 end
79
80 function set.schedStrat(obj, strat)
81 if (strcmp(strat, 'fcfs') ... % All stations have FCFS
82 || strcmp(strat, 'ps') ... % All stations have PS
83 || strcmp(strat, 'inf') ...
84 || strcmp(strat, 'lcfs') ...
85 || strcmp(strat, 'lcfspr') ...
86 || strcmp(strat, 'siro') ...
87 || strcmp(strat, 'sjf') ...
88 || strcmp(strat, 'ljf') ...
89 || strcmp(strat, 'sept') ...
90 || strcmp(strat, 'lept') ...
91 || strcmp(strat, 'randomize')) % Randomize across stations
92 obj.schedStrat = strat;
93 else
94 error('NG:schedStrat', ...
95 'Scheduling strategy does not exist or is not supported');
96 end
97 end
98
99 function set.routingStrat(obj, strat)
100 if (strcmp(strat, 'Probabilities') ... % All probabilistic routing
101 || strcmp(strat, 'Random') ... % All random routing
102 || strcmp(strat, 'randomize')) % Randomize across all classes and stations
103 obj.routingStrat = strat;
104 else
105 error('NG:routingStrat', ...
106 'Routing strategy does not exist or is not supported');
107 end
108 end
109
110 function set.distribution(obj, distrib)
111 if (strcmpi(distrib, 'Exp') ... % All exponentially distributed service distributions
112 || strcmpi(distrib, 'HyperExp') ... % All hyperexponentially distributed
113 || strcmpi(distrib, 'Erlang') ... % All Erlang distributed
114 || strcmpi(distrib, 'randomize')) % Randomize across all classes and stations
115 obj.distribution = distrib;
116 else
117 error('NG:distribution', ...
118 'Distribution does not exist or is not supported');
119 end
120 end
121
122 function set.cclassJobLoad(obj, load)
123 if (strcmpi(load, 'high') ... % All stations have high job load
124 || strcmpi(load, 'medium') ... % All stations have medium job load
125 || strcmpi(load, 'low') ... % All stations have low job load
126 || strcmpi(load, 'randomize')) % Randomize loads across stations
127 obj.cclassJobLoad = load;
128 else
129 error('NG:cclassJobLoad', ...
130 'Model load can only be high, medium, or low');
131 end
132 end
133 % Can be any function handle that takes an integer and returns a
134 % digraph object
135 function set.topologyFcn(obj, fcn)
136 try
137 graph = fcn(2);
138 catch
139 line_error(mfilename,'topologyFcn should take a positive integer and return a digraph');
140 end
141
142 if ~isa(graph, 'digraph') || numnodes(graph) ~= 2
143 line_error(mfilename,'topologyFcn should take a positive integer and return a digraph');
144 end
145
146 obj.topologyFcn = fcn;
147 end
148
149 end
150
151 methods (Access = private)
152 % Validates that parameter values for the network are sound
153 function validateArgs(~, numQueues, numDelays, numOClass, numCClass)
154 if numQueues < 0 || numDelays < 0 || numOClass < 0 || numCClass < 0
155 error('NG:negativeArgs', 'Arguments must be non-negative');
156 elseif numQueues + numDelays <= 0 || numOClass + numCClass <= 0
157 error('NG:noStationsOrJobs', 'At least one station and one job class required');
158 end
159 end
160
161 % Creates the stations in the network, including source and sink
162 function stations = createStations(obj, model, numQueues, numDelays, hasOClass)
163 queues = cell(numQueues, 1);
164 for i = 1 : numQueues
165 queues{i} = Queue(model, obj.name('queue', i), obj.chooseSchedStrat);
166 queues{i}.setNumberOfServers(chooseNumServers);
167 end
168
169 delays = cell(numDelays, 1);
170 for i = 1 : numDelays
171 delays{i} = Delay(model, obj.name('delay', i));
172 end
173
174 if hasOClass
175 Source(model, 'source');
176 Sink(model, 'sink');
177 end
178 % Source and sink intentionally excluded from station list
179 stations = [queues; delays];
180
181 function n = chooseNumServers
182 if obj.hasMultiServerQueues
183 n = randi(obj.maxServers);
184 else
185 n = 1;
186 end
187 end
188 end
189
190 % Creates the job classes that are serviced within the network
191 % Note: Arrival processes for open classes are set here for simplicity
192 function classes = createClasses(obj, model, numOClass, numCClass)
193 openClasses = cell(numOClass, 1);
194 for i = 1 : numOClass
195 openClasses{i} = OpenClass(model, obj.name('OClass', i));
196 model.getSource.setArrival(openClasses{i}, obj.chooseDistribution);
197 end
198
199 closedClasses = cell(numCClass, 1);
200 refStat = model.stations{randi(model.getNumberOfStations - (numOClass > 0))};
201 for i = 1 : numCClass
202 closedClasses{i} = ClosedClass(model, ...
203 obj.name('CClass', i), chooseNumJobs, refStat);
204 end
205
206 classes = [openClasses; closedClasses];
207
208 function numJobs = chooseNumJobs
209 switch lower(obj.cclassJobLoad)
210 case 'high'
211 numJobs = randi(obj.highJobLoadRange);
212 case 'medium'
213 numJobs = randi(obj.medJobLoadRange);
214 case 'low'
215 numJobs = randi(obj.lowJobLoadRange);
216 case 'randomize'
217 numJobs = randi(obj.highJobLoadRange(2));
218 end
219 end
220 end
221
222 % Sets the service processes for all job classes at all stations
223 % Note: Stations here excludes the Source node for simplicity
224 function setServiceProcesses(obj, stations, classes)
225 for i = 1 : length(classes)
226 for j = 1 : length(stations)
227 stations{j}.setService(classes{i}, obj.chooseDistribution);
228 end
229 end
230 end
231
232 % Defines the topology of the network, adding random class switching nodes
233 function defineTopology(obj, model, topology)
234 numStations = model.getNumberOfStations;
235 csMask = obj.genCSMask(model);
236 % Add source and sink to topology graph
237 if model.hasOpenClasses
238 topology = addnode(topology, 2);
239 topology = addedge(topology, model.getIndexSourceNode, randi(numStations - 1));
240 topology = addedge(topology, randi(numStations - 1), model.getIndexSinkNode);
241 end
242 for i = 1 : numStations
243 [~, destIDs] = outedges(topology, i);
244 outgoingNodes = obj.addOutgoingLinks(model, i, sort(destIDs), csMask);
245 obj.setRoutingStrategies(model, model.stations{i}, outgoingNodes);
246 end
247 end
248
249 % Creates a mask that indicates which classes can switch with each other
250 function mask = genCSMask(obj, model)
251 numOClasses = length(model.getIndexOpenClasses);
252 numCClasses = length(model.getIndexClosedClasses);
253 mask = zeros(numOClasses + numCClasses);
254
255 if ~obj.hasMultiChainCS
256 mask(1 : numOClasses, 1 : numOClasses) = 1;
257 mask(numOClasses + 1 : end, numOClasses + 1 : end) = 1;
258 mask = logical(mask);
259 return;
260 end
261
262 openChains = [];
263 closedChains = [];
264 if model.hasOpenClasses
265 openChains = assignChains(numOClasses, randi(numOClasses));
266 end
267 if model.hasClosedClasses
268 closedChains = assignChains(numCClasses, randi(numCClasses));
269 end
270 allChains = [openChains closedChains];
271
272 startIdx = 1;
273 for i = 1 : length(allChains)
274 endIdx = startIdx + allChains(i) - 1;
275 mask(startIdx : endIdx, startIdx : endIdx) = 1;
276 startIdx = endIdx + 1;
277 end
278
279 mask = logical(mask);
280
281 function chains = assignChains(numClasses, numChains)
282 chains = randintfixedsum(numClasses, numChains);
283 chains = chains(randperm(numChains));
284
285 function res = randintfixedsum(s, n)
286 if n == 1
287 res = s;
288 return;
289 elseif s == n
290 res = ones(1, n);
291 return;
292 end
293 first = randi(s - n);
294 res = [first randintfixedsum(s - first, n - 1)];
295 end
296 end
297 end
298
299 % For a single node, add all outgoing links and random class switch nodes
300 function outgoingNodes = addOutgoingLinks(obj, model, sourceID, allDestIDs, mask)
301 sourceNode = model.nodes{sourceID};
302 outgoingNodes = cell(length(allDestIDs), 1);
303
304 for i = 1 : length(allDestIDs)
305 destNode = model.nodes{allDestIDs(i)};
306 if obj.hasRandomCSNodes ...
307 && randi([0 1]) == 1 ...
308 && ~(isa(sourceNode, 'Source') || isa(destNode, 'Sink'))
309 cs = obj.randClassSwitchNode(model, mask, sourceNode, destNode);
310 outgoingNodes{i} = cs;
311 model.addLink(sourceNode, cs);
312 model.addLink(cs, destNode);
313 for j = 1 : model.getNumberOfClasses
314 cs.setProbRouting(model.classes{j}, destNode, 1);
315 end
316 else
317 outgoingNodes{i} = destNode;
318 model.addLink(sourceNode, destNode);
319 end
320 end
321 end
322
323 % Instantiates a class switch node with a random switching matrix
324 function cs = randClassSwitchNode(obj, model, mask, sourceNode, destNode)
325 name = obj.name('cs', sourceNode.getName, destNode.getName);
326 cs = ClassSwitch(model, name, randClassSwitchMatrix);
327
328 function matrix = randClassSwitchMatrix
329 matrix = zeros(length(mask));
330 for i = 1 : length(mask)
331 matrix(i, mask(i, :)) = obj.randfixedsumone(nnz(mask(i, :)));
332 end
333 end
334 end
335
336 % Set routing strategies for each job class at a specified station
337 function setRoutingStrategies(obj, model, station, outgoingNodes)
338 if isa(station, 'Source')
339 openClasses = model.classes(model.getIndexOpenClasses);
340 for i = 1 : length(openClasses)
341 for j = 1 : length(outgoingNodes)
342 station.setProbRouting(openClasses{i}, outgoingNodes{j}, 1);
343 end
344 end
345 return;
346 end
347
348 classes = model.classes;
349 for i = 1 : length(classes)
350 strat = obj.chooseRoutingStrat;
351 station.setRouting(classes{i}, RoutingStrategy.fromText(strat));
352 if strcmp(strat, 'Probabilities')
353 if isa(classes{i}, 'ClosedClass') && isa(outgoingNodes{end}, 'Sink')
354 station.setProbRouting(classes{i}, outgoingNodes{end}, 0);
355 probs = obj.randfixedsumone(length(outgoingNodes) - 1);
356 else
357 probs = obj.randfixedsumone(length(outgoingNodes));
358 end
359 for j = 1 : length(probs)
360 station.setProbRouting(classes{i}, outgoingNodes{j}, probs(j));
361 end
362 end
363 end
364 end
365
366 % Generate random probabilities that sum up to one
367 function probs = randfixedsumone(obj, numElems)
368 probs = randfixedsum(numElems, 1, 1, 0, 1);
369 probs = ceil(probs * 1000) / 1000;
370 [~, maxIdx] = max(probs);
371 probs(maxIdx) = probs(maxIdx) - (sum(probs) - 1);
372 end
373
374 % Creates a name string for network elements via concatenation
375 function name = name(~, str, varargin)
376 if strcmpi(str, 'cs')
377 name = strcat(str, '_', varargin{1}, '_', varargin{2});
378 else
379 name = strcat(str, int2str(varargin{1}));
380 end
381 end
382
383 % Returns a random scheduling strategy for a queueing station
384 function strat = chooseSchedStrat(obj)
385 if strcmpi(obj.schedStrat, 'randomize')
386 id = randi(2);
387 switch id
388 case 1
389 strat = SchedStrategy.FCFS;
390 case 2
391 strat = SchedStrategy.PS;
392 end
393 else
394 strat = categorical({obj.schedStrat});
395 end
396 end
397
398 % Returns a random routing strategy for a network node
399 function strat = chooseRoutingStrat(obj)
400 if strcmpi(obj.routingStrat, 'randomize')
401 id = randi(2);
402 switch id
403 case 1
404 strat = 'Random';
405 case 2
406 strat = 'Probabilities';
407 end
408 else
409 strat = obj.routingStrat;
410 end
411 end
412
413 % Returns a random distribution object for a service/arrival process
414 function dist = chooseDistribution(obj)
415 switch lower(obj.distribution)
416 case 'randomize'
417 id = randi(3);
418 case 'exp'
419 id = 1;
420 case 'erlang'
421 id = 2;
422 case 'hyperexp'
423 id = 3;
424 end
425
426 if obj.hasVaryingServiceRates
427 mean = 2 ^ randi([-6 6]);
428 else
429 mean = 1;
430 end
431
432 switch id
433 case 1
434 dist = Exp.fitMeanAndSCV(mean, 1);
435 case 2
436 dist = Erlang.fitMeanAndSCV(mean, 1/(2^randi([0 6])));
437 case 3
438 dist = HyperExp.fitMeanAndSCV(mean, 2^randi([0 6]));
439 end
440 end
441 end
442
443 methods (Static)
444 graph = randGraph(numVertices);
445 model = initDefaultCustom(model, nodes);
446 end
447end
Definition mmt.m:92