LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
MNetwork.m
1classdef MNetwork < Model
2 % An extended queueing network model.
3 %
4 % Copyright (c) 2012-2026, Imperial College London
5 % All rights reserved.
6
7 properties (Access=private)
8 enableChecks;
9 hasState;
10 logPath;
11 usedFeatures; % structure of booleans listing the used classes
12 % it must be accessed via getUsedLangFeatures that updates
13 % the Distribution classes dynamically
14 end
15
16 properties (Hidden)
17 obj; % empty
18 sn;
19 csMatrix;
20 hasStruct;
21 % Monotonic counter bumped every time the struct is recompiled or
22 % discarded. Consumers that cache something derived from the topology
23 % (e.g. SolverMVA's MMT fork-join transformation) tag the cache with the
24 % value seen at build time and reuse it only while the counter still
25 % matches, so any structural change forces a rebuild. Python and the JAR
26 % test identity of the NetworkStruct object for this; sn is a value
27 % struct in MATLAB, so it needs an explicit version instead.
28 structVersion = 0;
29 allowReplace;
30 forkStateful = false; % Fork nodes are stateful (FJ-augmented copies only, see ModelAdapter.fjtag)
31 isFJAugmented = false; % true on fork-join tag-augmented copies (skips the MMT visit correction)
32 end
33
34 properties (Hidden)
35 handles;
36 sourceidx; % cached value
37 sinkidx; % cached value
38 end
39
40 properties
41 classes;
42 items;
43 stations;
44 nodes;
45 connections;
46 regions;
47 end
48
49 methods % get methods
50 nodes = getNodes(self)
51 sa = getStruct(self, structType, wantState) % get abritrary representation
52 used = getUsedLangFeatures(self) % get used features
53 ft = getForkJoins(self, rt) % get fork-join pairs
54 [chainsObj,chainsMatrix] = getChains(self, rt) % get chain table
55 [rt,rtNodes,connections,chains,rtNodesByClass,rtNodesByStation] = getRoutingMatrix(self, arvRates) % get routing matrix
56 [Q,U,R,T,A,W] = getAvgHandles(self)
57 Q = getAvgQLenHandles(self);
58 U = getAvgUtilHandles(self);
59 R = getAvgRespTHandles(self);
60 T = getAvgTputHandles(self);
61 A = getAvgArvRHandles(self);
62 W = getAvgResidTHandles(self);
63 [Qt,Ut,Tt] = getTranHandles(self)
64 connections = getConnectionMatrix(self);
65 [absorbingStations, absorbingIdxs] = getAbsorbingStations(self) % get absorbing stations
66 info = getReducibilityInfo(self) % get reducibility analysis
67 end
68
69 methods % ergodicity analysis methods
70 [isErg, info] = isRoutingErgodic(self, P) % check if routing is ergodic
71 P = makeErgodic(self, targetNode) % generate ergodic routing matrix
72 end
73
74 methods % link, reset, refresh methods
75 self = link(self, P)
76 self = relink(self, P)
77 [loggerBefore,loggerAfter] = linkAndLog(self, nodes, classes, P, wantLogger, logPath)
78 sanitize(self);
79
80 reset(self, resetState)
81 resetHandles(self)
82 resetModel(self, resetState)
83 nodes = resetNetwork(self, deleteCSnodes)
84 resetStruct(self)
85
86 refreshStruct(self, hard);
87 [rates, scv, hasRateChanged, hasSCVChanged] = refreshRates(self, statSet, classSet);
88 [ph, mu, phi, phases] = refresProcessPhases(self, statSet, classSet);
89 proctypes = refreshProcessTypes(self);
90 [rt, rtfun, rtnodes] = refreshRoutingMatrix(self, rates);
91 [lt] = refreshLST(self, statSet, classSet);
92 sync = refreshSync(self);
93 classprio = refreshPriorities(self);
94 [sched, schedparam] = refreshScheduling(self);
95 [rates, scv, mu, phi, phases] = refreshProcesses(self, statSet, classSet);
96 [chains, visits, rt] = refreshChains(self, propagate)
97 [cap, classcap] = refreshCapacity(self);
98 nvars = refreshLocalVars(self);
99 end
100
101 % PUBLIC METHODS
102 methods (Access=public)
103
104 %Constructor
105 function self = MNetwork(modelName, varargin)
106 % SELF = NETWORK(MODELNAME)
107 self@Model(modelName);
108 self.nodes = {};
109 self.stations = {};
110 self.classes = {};
111 self.connections = [];
112 initUsedFeatures(self);
113 self.sn = [];
114 self.hasState = false;
115 self.logPath = '';
116 self.items = {};
117 self.regions = {};
118 self.sourceidx = [];
119 self.sinkidx = [];
120 self.setChecks(true);
121 self.hasStruct = false;
122 self.allowReplace = false;
123 end
124
125 setInitialized(self, bool);
126
127 function self = setChecks(self, bool)
128 self.enableChecks = bool;
129 end
130
131 P = getLinkedRoutingMatrix(self)
132
133 function logPath = getLogPath(self)
134 % LOGPATH = GETLOGPATH()
135
136 logPath = self.logPath;
137 end
138
139 function setLogPath(self, logPath)
140 % SETLOGPATH(LOGPATH)
141
142 self.logPath = logPath;
143 end
144
145 bool = hasInitState(self)
146
147 function [M,R] = getSize(self)
148 % [M,R] = GETSIZE()
149
150 M = self.getNumberOfNodes;
151 R = self.getNumberOfClasses;
152 end
153
154 function bool = hasOpenClasses(self)
155 % BOOL = HASOPENCLASSES()
156
157 bool = any(isinf(getNumberOfJobs(self)));
158 end
159
160 function bool = hasClassSwitching(self)
161 % BOOL = HASCLASSSWITCHING()
162
163 bool = any(cellfun(@(c) isa(c,'ClassSwitch'), self.nodes));
164 end
165
166 function bool = hasFork(self)
167 % BOOL = HASFORK()
168
169 bool = any(cellfun(@(c) isa(c,'Fork'), self.nodes));
170 end
171
172 function bool = hasJoin(self)
173 % BOOL = HASJOIN()
174
175 bool = any(cellfun(@(c) isa(c,'Join'), self.nodes));
176 end
177
178 function bool = hasClosedClasses(self)
179 % BOOL = HASCLOSEDCLASSES()
180
181 bool = any(isfinite(getNumberOfJobs(self)));
182 end
183
184 function bool = isMatlabNative(self)
185 % BOOL = ISMATLABNATIVE()
186 %
187 % Returns true for MATLAB native (MNetwork) implementation
188
189 bool = true;
190 end
191
192 function bool = isJavaNative(self)
193 % BOOL = ISJAVANATIVE()
194 %
195 % Returns false for MATLAB native (MNetwork) implementation
196
197 bool = false;
198 end
199
200 function index = getIndexOpenClasses(self)
201 % INDEX = GETINDEXOPENCLASSES()
202
203 index = find(isinf(getNumberOfJobs(self)))';
204 end
205
206 function index = getIndexClosedClasses(self)
207 % INDEX = GETINDEXCLOSEDCLASSES()
208
209 index = find(isfinite(getNumberOfJobs(self)))';
210 end
211
212 function classes = getClasses(self)
213 classes= self.classes;
214 end
215
216 chain = getClassChain(self, className)
217 c = getClassChainIndex(self, className)
218 classNames = getClassNames(self)
219
220 nodeNames = getNodeNames(self)
221 nodeTypes = getNodeTypes(self)
222
223 P = initRoutingMatrix(self)
224
225 ind = getNodeIndex(self, name)
226 lldScaling = getLimitedLoadDependence(self)
227 lcdScaling = getLimitedClassDependence(self)
228
229 function stationIndex = getStationIndex(self, name)
230 % STATIONINDEX = GETSTATIONINDEX(NAME)
231
232 if isa(name,'Node')
233 node = name;
234 name = node.getName();
235 end
236 stationIndex = find(cellfun(@(c) strcmp(c,name),self.getStationNames));
237 end
238
239 function statefulIndex = getStatefulNodeIndex(self, name)
240 % STATEFULINDEX = GETSTATEFULNODEINDEX(NAME)
241
242 if isa(name,'Node')
243 node = name;
244 name = node.getName();
245 end
246 statefulIndex = find(cellfun(@(c) strcmp(c,name),self.getStatefulNodeNames));
247 end
248
249 function classIndex = getClassIndex(self, name)
250 % CLASSINDEX = GETCLASSINDEX(NAME)
251 if isa(name,'JobClass')
252 jobclass = name;
253 name = jobclass.getName();
254 end
255 classIndex = find(cellfun(@(c) strcmp(c,name),self.getClassNames));
256 end
257
258 function stationnames = getStationNames(self)
259 % STATIONNAMES = GETSTATIONNAMES()
260
261 if self.hasStruct
262 nodenames = self.sn.nodenames;
263 isstation = self.sn.isstation;
264 stationnames = {nodenames{isstation}}';
265 else
266 stationnames = {};
267 for i=self.getStationIndexes
268 stationnames{end+1,1} = self.nodes{i}.name;
269 end
270 end
271 end
272
273 function nodes = getNodeByName(self, name)
274 % NODES = GETNODEBYNAME(SELF, NAME)
275 idx = findstring(self.getNodeNames,name);
276 if idx > 0
277 nodes = self.nodes{idx};
278 else
279 nodes = NaN;
280 end
281 end
282
283 function station = getStationByName(self, name)
284 % STATION = GETSTATIONBYNAME(SELF, NAME)
285 idx = findstring(self.getStationNames,name);
286 if idx > 0
287 station = self.stations{idx};
288 else
289 station = NaN;
290 end
291 end
292
293 function class = getClassByName(self, name)
294 % CLASS = GETCLASSBYNAME(SELF, NAME)
295 idx = findstring(self.getClassNames,name);
296 if idx > 0
297 class = self.classes{idx};
298 else
299 class = NaN;
300 end
301 end
302
303 function nodes = getNodeByIndex(self, idx)
304 % NODES = GETNODEBYINDEX(SELF, NAME)
305 if idx > 0
306 nodes = self.nodes{idx};
307 else
308 nodes = NaN;
309 end
310 end
311
312 function station = getStationByIndex(self, idx)
313 % STATION = GETSTATIONBYINDEX(SELF, NAME)
314 if idx > 0
315 station = self.stations{idx};
316 else
317 station = NaN;
318 end
319 end
320
321 function class = getClassByIndex(self, idx)
322 % CLASS = GETCLASSBYINDEX(SELF, NAME)
323 if idx > 0
324 class = self.classes{idx};
325 else
326 class = NaN;
327 end
328 end
329
330 function [infGen, eventFilt, ev] = getGenerator(self, varargin)
331 line_warning(mfilename,'Results will not be cached. Use SolverCTMC(model,...).getGenerator(...) instead.\n');
332 [infGen, eventFilt, ev] = SolverCTMC(self).getGenerator(varargin{:});
333 end
334
335 function [stateSpace,nodeStateSpace] = getStateSpace(self, varargin)
336 line_warning(mfilename,'Results will not be cached. Use SolverCTMC(model,...).getStateSpace(...) instead.\n');
337 [stateSpace,nodeStateSpace] = SolverCTMC(self).getStateSpace(varargin{:});
338 end
339
340 function [stateSpace,nodeStateSpace] = stateSpace(self, varargin)
341 % [STATESPACE,NODESTATESPACE] = STATESPACE(SELF, VARARGIN)
342 % Alias for getStateSpace
343 [stateSpace,nodeStateSpace] = self.getStateSpace(varargin{:});
344 end
345
346 function summary(self)
347 % SUMMARY()
348 for i=1:self.getNumberOfClasses
349 self.classes{i}.summary();
350 if i<self.getNumberOfClasses
351 line_printf('\n');
352 end
353 end
354 for i=1:self.getNumberOfNodes
355 self.nodes{i}.summary();
356 end
357 line_printf('\n<strong>Routing matrix</strong>:');
358 self.printRoutingMatrix
359 line_printf('\n<strong>Product-form parameters</strong>:');
360 [arvRates,servDemands,nJobs,thinkTimes,ldScalings,nServers]= getProductFormParameters(self);
361 line_printf('\nArrival rates: %s',mat2str(arvRates,6));
362 line_printf('\nService demands: %s',mat2str(servDemands,6));
363 line_printf('\nNumber of jobs: %s',mat2str(nJobs));
364 line_printf('\nThink times: %s',mat2str(thinkTimes,6));
365 line_printf('\nLoad-dependent scalings: %s',mat2str(ldScalings,6));
366 line_printf('\nNumber of servers: %s\n',mat2str(nServers));
367 line_printf('\n<strong>Chains</strong>:');
368 end
369
370 function [D,Z] = getDemands(self)
371 % [D,Z]= GETDEMANDS()
372 %
373 % Outputs:
374 % D: service demands
375 % Z: think times
376
377 [~,D,~,Z,~,~] = sn_get_product_form_params(self.getStruct);
378 end
379
380 function [lambda,D,N,Z,mu,S]= getProductFormParameters(self)
381 % [LAMBDA,D,N,Z,MU,S]= GETPRODUCTFORMPARAMETERS()
382 %
383 % Outputs:
384 % LAMBDA: arrival rates for open classes
385 % D: service demands
386 % N: population vector for closed classes
387 % Z: think times
388 % mu: load-dependent rates
389 % S: number of servers
390
391 % mu also returns max(S) elements after population |N| as this is
392 % required by MVALDMX
393
394 [lambda,D,N,Z,mu,S] = sn_get_product_form_params(self.getStruct);
395 end
396
397 function [lambda,D,N,Z,mu,S]= getProductFormChainParameters(self)
398 % [LAMBDA,D,N,Z,MU,S]= GETPRODUCTFORMCHAINPARAMETERS()
399 %
400 % Outputs:
401 % LAMBDA: arrival rates for open classes
402 % D: service demands
403 % N: population vector for closed classes
404 % Z: think times
405 % mu: load-dependent rates
406 % S: number of servers
407
408 % mu also returns max(S) elements after population |N| as this is
409 % required by MVALDMX
410
411 [lambda,D,N,Z,mu,S]= sn_get_product_form_chain_params(self.getStruct);
412 end
413
414 function statefulnodes = getStatefulNodes(self)
415 statefulnodes = {};
416 for i=1:self.getNumberOfNodes
417 if self.nodes{i}.isStateful
418 statefulnodes{end+1,1} = self.nodes{i};
419 end
420 end
421 end
422
423 function statefulnames = getStatefulNodeNames(self)
424 % STATEFULNAMES = GETSTATEFULNODENAMES()
425
426 statefulnames = {};
427 for i=1:self.getNumberOfNodes
428 if self.nodes{i}.isStateful
429 statefulnames{end+1,1} = self.nodes{i}.name;
430 end
431 end
432 end
433
434 function M = getNumberOfNodes(self)
435 % M = GETNUMBEROFNODES()
436
437 M = length(self.nodes);
438 end
439
440 function S = getNumberOfStatefulNodes(self)
441 % S = GETNUMBEROFSTATEFULNODES()
442
443 S = sum(cellisa(self.nodes,'StatefulNode'));
444 end
445
446 function M = getNumberOfStations(self)
447 % M = GETNUMBEROFSTATIONS()
448
449 M = length(self.stations);
450 end
451
452 function R = getNumberOfClasses(self)
453 % R = GETNUMBEROFCLASSES()
454
455 R = length(self.classes);
456 end
457
458 function C = getNumberOfChains(self)
459 % C = GETNUMBEROFCHAINS()
460
461 sn = self.getStruct;
462 C = sn.nchains;
463 end
464
465 function Dchain = getDemandsChain(self)
466 % DCHAIN = GETDEMANDSCHAIN()
467 sn_get_demands_chain(self.getStruct);
468 end
469
470 function self = setUsedLangFeature(self,className)
471 % SELF = SETUSEDLANGFEATURE(SELF,CLASSNAME)
472
473 % Normalize marked-process class names to the registered MMAP
474 % feature; dynamic names absent from SolverFeatureSet.fields are
475 % silently ignored by SolverFeatureSet.supports().
476 if strcmp(className,'MarkedMAP') || strcmp(className,'MarkedMMPP')
477 className = 'MMAP';
478 end
479 self.usedFeatures.setTrue(className);
480 end
481
482 %% Add the components to the model
483 addJobClass(self, customerClass);
484 bool = addNode(self, node);
485 fcr = addRegion(self, nodes);
486 addLink(self, nodeA, nodeB);
487 addLinks(self, nodeList);
488 addItemSet(self, itemSet);
489
490 node = getSource(self);
491 node = getSink(self);
492
493 function list = getStationIndexes(self)
494 % LIST = GETSTATIONINDEXES()
495
496 if self.hasStruct
497 list = find(self.sn.isstation)';
498 else
499 % returns the ids of nodes that are stations
500 list = find(cellisa(self.nodes, 'Station'))';
501 end
502 end
503
504 function list = getIndexStatefulNodes(self)
505 % LIST = GETINDEXSTATEFULNODES()
506
507 % returns the ids of nodes that are stations
508 list = find(cellisa(self.nodes, 'StatefulNode'))';
509 if self.forkStateful
510 % FJ tag-augmented copies treat Fork nodes as stateful
511 list = union(list, find(cellisa(self.nodes, 'Fork'))');
512 end
513 end
514
515 index = getIndexSourceStation(self);
516 index = getIndexSourceNode(self);
517 index = getIndexSinkNode(self);
518
519 N = getNumberOfJobs(self);
520 refstat = getReferenceStations(self);
521 refclass = getReferenceClasses(self);
522 sched = getStationScheduling(self);
523 S = getStationServers(self);
524 S = getStatefulServers(self);
525
526 jsimwView(self)
527 jsimgView(self)
528 function view(self)
529 % VIEW() Open the model in JSIMgraph
530 jsimgView(self);
531 end
532
533 function modelView(self)
534 % MODELVIEW() Open the model in ModelVisualizer
535 jnetwork = JLINE.line_to_jline(self);
536 jnetwork.plot();
537 end
538
539 function islld = isLimitedLoadDependent(self)
540 islld = isempty(self.getStruct().lldscaling);
541 end
542
543 function [isvalid] = isStateValid(self)
544 % [ISVALID] = ISSTATEVALID()
545
546 isvalid = sn_is_state_valid(self.getStruct);
547 end
548
549 [state, priorStateSpace, stateSpace] = getState(self) % get initial state
550
551 initFromAvgTableQLen(self, AvgTable)
552 initFromAvgQLen(self, AvgQLen)
553 initDefault(self, nodes)
554
555 initFromMarginal(self, n, options) % n(i,r) : number of jobs of class r in node or station i (autodetected)
556 initFromMarginalAndRunning(self, n, s, options) % n(i,r) : number of jobs of class r in node or station i (autodetected)
557 initFromMarginalAndStarted(self, n, s, options) % n(i,r) : number of jobs of class r in node or station i (autodetected)
558
559 [H,G] = getGraph(self)
560
561 function mask = getClassSwitchingMask(self)
562 % MASK = GETCLASSSWITCHINGMASK()
563
564 mask = self.getStruct.csmask;
565 end
566
567 function printRoutingMatrix(self, onlyclass)
568 % PRINTROUTINGMATRIX()
569 if nargin==1
570 sn_print_routing_matrix(self.getStruct);
571 else
572 sn_print_routing_matrix(self.getStruct, onlyclass);
573 end
574 end
575 end
576
577 % Private methods
578 methods (Access = protected)
579
580 function out = getModelNameExtension(self)
581 % OUT = GETMODELNAMEEXTENSION()
582
583 out = [getModelName(self), ['.', self.fileFormat]];
584 end
585
586 function self = initUsedFeatures(self)
587 % SELF = INITUSEDFEATURES()
588
589 % The list includes all classes but Model and Hidden or
590 % Constant or Abstract or Solvers
591 self.usedFeatures = SolverFeatureSet;
592 end
593 end
594
595 methods(Access = protected)
596 % Override copyElement method:
597 function clone = copyElement(self)
598 % CLONE = COPYELEMENT()
599
600 % Make a shallow copy of all properties
601 clone = copyElement@Copyable(self);
602 % Make a deep copy of each handle
603 for i=1:length(self.classes)
604 clone.classes{i} = self.classes{i}.copy;
605 end
606 % Make a deep copy of each handle
607 for i=1:length(self.nodes)
608 clone.nodes{i} = self.nodes{i}.copy;
609 if isa(clone.nodes{i},'Station')
610 clone.stations{i} = clone.nodes{i};
611 end
612 clone.connections = self.connections;
613 end
614 end
615 end
616
617 methods % wrappers
618 function bool = hasFCFS(self)
619 % BOOL = HASFCFS()
620
621 bool = sn_has_fcfs(self.getStruct);
622 end
623
624 function bool = hasHomogeneousScheduling(self, strategy)
625 % BOOL = HASHOMOGENEOUSSCHEDULING(STRATEGY)
626
627 bool = sn_has_homogeneous_scheduling(self.getStruct, strategy);
628 end
629
630 function bool = hasDPS(self)
631 % BOOL = HASDPS()
632
633 bool = sn_has_dps(self.getStruct);
634 end
635
636 function bool = hasGPS(self)
637 % BOOL = HASGPS()
638
639 bool = sn_has_gps(self.getStruct);
640 end
641
642 function bool = hasINF(self)
643 % BOOL = HASINF()
644
645 bool = sn_has_inf(self.getStruct);
646 end
647
648 function bool = hasPS(self)
649 % BOOL = HASPS()
650
651 bool = sn_has_ps(self.getStruct);
652 end
653
654 function bool = hasSIRO(self)
655 % BOOL = HASSIRO()
656
657 bool = sn_has_siro(self.getStruct);
658 end
659
660 function bool = hasHOL(self)
661 % BOOL = HASHOL()
662
663 bool = sn_has_hol(self.getStruct);
664 end
665
666 function bool = hasLCFS(self)
667 % BOOL = HASLCFS()
668
669 bool = sn_has_lcfs(self.getStruct);
670 end
671
672 function bool = hasLCFSPR(self)
673 % BOOL = HASLCFSPR()
674
675 bool = sn_has_lcfs_pr(self.getStruct);
676 end
677
678 function bool = hasSEPT(self)
679 % BOOL = HASSEPT()
680
681 bool = sn_has_sept(self.getStruct);
682 end
683
684 function bool = hasLEPT(self)
685 % BOOL = HASLEPT()
686
687 bool = sn_has_lept(self.getStruct);
688 end
689
690 function bool = hasSJF(self)
691 % BOOL = HASSJF()
692
693 bool = sn_has_sjf(self.getStruct);
694 end
695
696 function bool = hasLJF(self)
697 % BOOL = HASLJF()
698
699 bool = sn_has_ljf(self.getStruct);
700 end
701
702 function bool = hasMultiClassFCFS(self)
703 % BOOL = HASMULTICLASSFCFS()
704
705 bool = sn_has_multi_class_fcfs(self.getStruct);
706 end
707
708 function bool = hasMultiClassHeterFCFS(self)
709 % BOOL = HASMULTICLASSFCFS()
710
711 bool = sn_has_multi_class_heter_fcfs(self.getStruct);
712 end
713
714 function bool = hasMultiServer(self)
715 % BOOL = HASMULTISERVER()
716
717 bool = sn_has_multi_server(self.getStruct);
718 end
719
720 function bool = hasSingleChain(self)
721 % BOOL = HASSINGLECHAIN()
722
723 bool = sn_has_single_chain(self.getStruct);
724 end
725
726 function bool = hasMultiChain(self)
727 % BOOL = HASMULTICHAIN()
728
729 bool = sn_has_multi_chain(self.getStruct);
730 end
731
732 function bool = hasSingleClass(self)
733 % BOOL = HASSINGLECLASS()
734
735 bool = sn_has_single_class(self.getStruct);
736 end
737
738 function bool = hasMultiClass(self)
739 % BOOL = HASMULTICLASS()
740
741 bool = sn_has_multi_class(self.getStruct);
742 end
743 end
744
745 methods
746 function bool = hasProductFormSolution(self)
747 % BOOL = HASPRODUCTFORMSOLUTION()
748 bool = sn_has_product_form(self.getStruct);
749 end
750
751 function plot(self)
752 % PLOT()
753 [~,H] = self.getGraph;
754 H.Nodes.Name=strrep(H.Nodes.Name,'_','\_');
755 h=plot(H,'EdgeLabel',H.Edges.Weight,'Layout','Layered');
756 highlight(h,self.getNodeTypes==3,'NodeColor','r'); % class-switch nodes
757 end
758
759 function varargout = getMarkedCTMC( varargin )
760 [varargout{1:nargout}] = getCTMC( varargin{:} );
761 end
762
763 function mctmc = getCTMC(self, par1, par2)
764 if nargin<2
765 options = SolverCTMC.defaultOptions;
766 elseif ischar(par1)
767 options = SolverCTMC.defaultOptions;
768 options.(par1) = par2;
769 options.cache = false;
770 elseif isstruct(par1)
771 options = par1;
772 end
773 solver = SolverCTMC(self,options);
774 [infGen, eventFilt, ev] = solver.getInfGen();
775 mctmc = MarkedMarkovProcess(infGen, eventFilt, ev, true);
776 mctmc.setStateSpace(solver.getStateSpace);
777 end
778 end
779
780 methods (Static)
781
782 function model = tandemPs(lambda,D)
783 % MODEL = TANDEMPS(LAMBDA,D)
784
785 model = Network.tandemPsInf(lambda,D,[]);
786 end
787
788 function model = tandemPsInf(lambda,D,Z)
789 % MODEL = TANDEMPSINF(LAMBDA,D,Z)
790
791 if nargin<3%~exist('Z','var')
792 Z = [];
793 end
794 M = size(D,1);
795 Mz = size(Z,1);
796 strategy = {};
797 for i=1:Mz
798 strategy{i} = SchedStrategy.INF;
799 end
800 for i=1:M
801 strategy{Mz+i} = SchedStrategy.PS;
802 end
803 model = Network.tandem(lambda,[Z;D],strategy);
804 end
805
806 function model = tandemFcfs(lambda,D)
807 % MODEL = TANDEMFCFS(LAMBDA,D)
808
809 model = Network.tandemFcfsInf(lambda,D,[]);
810 end
811
812 function model = tandemFcfsInf(lambda,D,Z)
813 % MODEL = TANDEMFCFSINF(LAMBDA,D,Z)
814
815 if nargin<3%~exist('Z','var')
816 Z = [];
817 end
818 M = size(D,1);
819 Mz = size(Z,1);
820 strategy = {};
821 for i=1:Mz
822 strategy{i} = SchedStrategy.INF;
823 end
824 for i=1:M
825 strategy{Mz+i} = SchedStrategy.FCFS;
826 end
827 model = Network.tandem(lambda,[Z;D],strategy);
828 end
829
830 function model = tandem(lambda,D,strategy)
831 % MODEL = TANDEM(LAMBDA,S,STRATEGY)
832
833 % D(i,r) - mean service demand of class r at station i, equal
834 % to mean service time since the topology is linear
835 % lambda(r) - number of jobs of class r
836 % station(i) - scheduling strategy at station i
837 model = Network('Model');
838 [M,R] = size(D);
839 node{1} = Source(model, 'Source');
840 for i=1:M
841 switch SchedStrategy.toId(strategy{i})
842 case SchedStrategy.INF
843 node{end+1} = Delay(model, ['Station',num2str(i)]);
844 otherwise
845 node{end+1} = Queue(model, ['Station',num2str(i)], strategy{i});
846 end
847 end
848 node{end+1} = Sink(model, 'Sink');
849 P = cellzeros(R,R,M+2,M+2);
850 for r=1:R
851 jobclass{r} = OpenClass(model, ['Class',num2str(r)], 0);
852 P{r,r} = circul(length(node)); P{r}(end,:) = 0;
853 end
854 for r=1:R
855 node{1}.setArrival(jobclass{r}, Exp.fitMean(1/lambda(r)));
856 for i=1:M
857 node{1+i}.setService(jobclass{r}, Exp.fitMean(D(i,r)));
858 end
859 end
860 model.link(P);
861 end
862
863 function model = cyclicPs(N,D)
864 % MODEL = CYCLICPS(N,D)
865 M = size(D,1);
866 if nargin<4
867 S = ones(M,1);
868 end
869 model = Network.cyclicPsInf(N,D,[],S);
870 end
871
872 function model = cyclicPsInf(N,D,Z,S)
873 % MODEL = CYCLICPSINF(N,D,Z,S)
874 if nargin<3
875 Z = [];
876 end
877 M = size(D,1);
878 if nargin<4
879 S = ones(M,1);
880 end
881 Mz = size(Z,1);
882 strategy = cell(M+Mz,1);
883 for i=1:Mz
884 strategy{i} = SchedStrategy.INF;
885 end
886 for i=1:M
887 strategy{Mz+i} = SchedStrategy.PS;
888 end
889 model = Network.cyclic(N,[Z;D],strategy,[Inf*ones(size(Z,1),1);S]);
890 end
891
892 function model = cyclicFcfs(N,D,S)
893 % MODEL = CYCLICFCFS(N,D,S)
894 M = size(D,1);
895 if nargin<4
896 S = ones(M,1);
897 end
898 model = Network.cyclicFcfsInf(N,D,[],S);
899 end
900
901 function model = cyclicFcfsInf(N,D,Z,S)
902 % MODEL = CYCLICFCFSINF(N,D,Z,S)
903
904 if nargin<3%~exist('Z','var')
905 Z = [];
906 end
907 M = size(D,1);
908 if nargin<4
909 S = ones(M,1);
910 end
911
912 Mz = size(Z,1);
913 strategy = {};
914 for i=1:Mz
915 strategy{i} = SchedStrategy.INF;
916 end
917 for i=1:M
918 strategy{Mz+i} = SchedStrategy.FCFS;
919 end
920 model = Network.cyclic(N,[Z;D],strategy,[Inf*ones(size(Z,1),1);S]);
921 end
922
923 function model = cyclic(N,D,strategy,S)
924 % MODEL = CYCLIC(N,D,STRATEGY,S)
925
926 % L(i,r) - demand of class r at station i
927 % N(r) - number of jobs of class r
928 % strategy(i) - scheduling strategy at station i
929 % S(i) - number of servers at station i
930 model = Network('Model');
931 [M,R] = size(D);
932 node = cell(M,1);
933 nQ = 0; nD = 0;
934 for ist=1:M
935 switch SchedStrategy.toId(strategy{ist})
936 case SchedStrategy.INF
937 nD = nD + 1;
938 node{ist} = Delay(model, ['Delay',num2str(nD)]);
939 otherwise
940 nQ = nQ + 1;
941 node{ist} = Queue(model, ['Queue',num2str(nQ)], strategy{ist});
942 node{ist}.setNumberOfServers(S(ist));
943 end
944 end
945 P = cellzeros(R,M);
946 jobclass = cell(R,1);
947 for r=1:R
948 jobclass{r} = ClosedClass(model, ['Class',num2str(r)], N(r), node{1}, 0);
949 P{r,r} = circul(M);
950 end
951 for ist=1:M
952 for r=1:R
953 node{ist}.setService(jobclass{r}, Exp.fitMean(D(ist,r)));
954 end
955 end
956 model.link(P);
957 end
958
959 function model = cluster(lambda, D, strategy, S, dispatching)
960 % MODEL = SERVERFARM(LAMBDA, D, STRATEGY, S, DISPATCHING)
961 %
962 % Open cluster: Source -> Dispatcher -> Server[1..M] -> Sink.
963 %
964 % lambda(r) - arrival rate of class r
965 % D(i,r) - mean service time of class r at server i
966 % strategy{i} - scheduling strategy at server i
967 % S(i) - number of identical servers at queue i (default: 1)
968 % dispatching - RoutingStrategy applied at the router (default: RAND)
969 if nargin < 4 || isempty(S), S = ones(size(D,1),1); end
970 if nargin < 5, dispatching = RoutingStrategy.RAND; end
971
972 model = Network('Cluster');
973 [M,R] = size(D);
974
975 source = Source(model, 'Source');
976 dispatcher = Router(model, 'Dispatcher');
977 servers = cell(M,1);
978 for i = 1:M
979 servers{i} = Queue(model, ['Station', num2str(i)], strategy{i});
980 if S(i) > 1
981 servers{i}.setNumberOfServers(S(i));
982 end
983 end
984 sink = Sink(model, 'Sink');
985
986 jobclass = cell(R,1);
987 for r = 1:R
988 jobclass{r} = OpenClass(model, ['Class', num2str(r)], 0);
989 source.setArrival(jobclass{r}, Exp.fitMean(1/lambda(r)));
990 for i = 1:M
991 servers{i}.setService(jobclass{r}, Exp.fitMean(D(i,r)));
992 end
993 end
994
995 model.addLink(source, dispatcher);
996 for i = 1:M
997 model.addLink(dispatcher, servers{i});
998 model.addLink(servers{i}, sink);
999 end
1000 for r = 1:R
1001 dispatcher.setRouting(jobclass{r}, dispatching);
1002 end
1003 end
1004
1005 function model = clusterClosed(N, Z, D, strategy, S, dispatching)
1006 % MODEL = SERVERFARMCLOSED(N, Z, D, STRATEGY, S, DISPATCHING)
1007 %
1008 % Closed cluster: Think -> Dispatcher -> Server[1..M] -> Think.
1009 %
1010 % N(r) - population of class r
1011 % Z(r) - per-class think time at the delay
1012 % D(i,r) - mean service time of class r at server i
1013 % strategy{i} - scheduling strategy at server i
1014 % S(i) - number of identical servers at queue i (default: 1)
1015 % dispatching - RoutingStrategy applied at the router (default: RAND)
1016 if nargin < 5 || isempty(S), S = ones(size(D,1),1); end
1017 if nargin < 6, dispatching = RoutingStrategy.RAND; end
1018
1019 model = Network('Cluster');
1020 [M,R] = size(D);
1021
1022 think = Delay(model, 'Think');
1023 dispatcher = Router(model, 'Dispatcher');
1024 servers = cell(M,1);
1025 for i = 1:M
1026 servers{i} = Queue(model, ['Station', num2str(i)], strategy{i});
1027 if S(i) > 1
1028 servers{i}.setNumberOfServers(S(i));
1029 end
1030 end
1031
1032 jobclass = cell(R,1);
1033 for r = 1:R
1034 jobclass{r} = ClosedClass(model, ['Class', num2str(r)], N(r), think, 0);
1035 think.setService(jobclass{r}, Exp.fitMean(Z(r)));
1036 for i = 1:M
1037 servers{i}.setService(jobclass{r}, Exp.fitMean(D(i,r)));
1038 end
1039 end
1040
1041 model.addLink(think, dispatcher);
1042 for i = 1:M
1043 model.addLink(dispatcher, servers{i});
1044 model.addLink(servers{i}, think);
1045 end
1046 for r = 1:R
1047 dispatcher.setRouting(jobclass{r}, dispatching);
1048 end
1049 end
1050
1051 function P = serialRouting(varargin)
1052 % P = SERIALROUTING(VARARGIN)
1053
1054 if length(varargin)==1
1055 varargin = varargin{1};
1056 end
1057 model = varargin{1}.model;
1058 P = zeros(model.getNumberOfNodes);
1059 for i=1:length(varargin)-1
1060 P(varargin{i},varargin{i+1})=1;
1061 end
1062 % Auto-close the cycle (last -> first) unless the last node is a Sink
1063 % or the caller already closed the loop by repeating the first node as
1064 % the last (e.g. serialRouting(d,q,d)), which would otherwise add a
1065 % spurious self-loop. Mirrors the Python serial_routing guard.
1066 if ~isa(varargin{end},'Sink') && varargin{end} ~= varargin{1}
1067 P(varargin{end},varargin{1})=1;
1068 end
1069 P = P ./ repmat(sum(P,2),1,length(P));
1070 P(isnan(P)) = 0;
1071 end
1072
1073 function printInfGen(Q,SS)
1074 % PRINTINFGEN(Q,SS)
1075 SolverCTMC.printInfGen(Q,SS);
1076 end
1077
1078 function printEventFilt(sync,D,SS,myevents)
1079 % PRINTEVENTFILT(SYNC,D,SS,MYEVENTS)
1080 SolverCTMC.printEventFilt(sync,D,SS,myevents);
1081 end
1082
1083 end
1084end
Definition fjtag.m:157
Definition Station.m:245