1function linemodel_save(model, filename)
2% LINEMODEL_SAVE Save a LINE model (Network or LayeredNetwork) to JSON.
4% LINEMODEL_SAVE(MODEL, FILENAME) saves the model to the specified JSON
5% file, conforming to the line-model.schema.json specification.
8% model - Network or LayeredNetwork
object
9% filename - output file path (should end in .json)
12% model = Network(
'M/M/1');
13% source = Source(model,
'Source');
14% queue = Queue(model,
'Queue', SchedStrategy.FCFS);
15% sink = Sink(model,
'Sink');
16% oclass = OpenClass(model,
'Class1');
17% source.setArrival(oclass, Exp(1.0));
18% queue.setService(oclass, Exp(2.0));
19%
P = model.initRoutingMatrix();
20%
P{1}(1,2) = 1;
P{1}(2,3) = 1;
22% linemodel_save(model,
'mm1.json');
24% Copyright (c) 2012-2026, Imperial College London
27if isa(model,
'LayeredNetwork')
28 modelMap = layered2json(model);
30 modelMap = network2json(model);
33% Build the full document
36sb{end+1} =
' "format": "line-model",';
37sb{end+1} =
' "version": "1.0",';
38sb{end+1} = [
' "model": ', encode_value(modelMap, 2)];
40jsonStr = strjoin(sb, newline);
42fid = fopen(filename,
'w');
44 error(
'linemodel_save:fileOpen',
'Cannot open file: %s', filename);
46cleanupObj = onCleanup(@() fclose(fid));
47fprintf(fid,
'%s\n', jsonStr);
51% =========================================================================
52% Network serialization
53% =========================================================================
55function result = network2json(model)
56% Convert a Network to a containers.Map (preserves key ordering/commas)
57result = containers.Map();
58result(
'type') =
'Network';
59result(
'name') = model.getName();
61nodes = model.getNodes();
71 % Skip implicit ClassSwitch
nodes (
auto-created by link())
72 if isa(node, 'ClassSwitch') && isprop(node, 'autoAdded') && node.autoAdded
76 nj = containers.Map();
77 nj('name') = node.name;
78 nj('type') = node_type_str(node);
82 nj('scheduling') = 'INF';
83 elseif isa(node, 'Queue')
84 sched = node.schedStrategy;
86 nj('scheduling') = sched_id_to_str(sched);
91 if isa(node, 'Queue') && ~isa(node, 'Delay')
92 ns = node.numberOfServers;
93 if isfinite(ns) && ns > 1
101 if ~isempty(c) && isfinite(c) && c > 0
106 % Service / arrival distributions
107 svc = containers.Map();
111 if isa(node,
'Source')
113 dist = node.getArrivalProcess(jc);
117 elseif isa(node, 'Queue') || isa(node, 'Delay')
119 dist = node.getService(jc);
124 if ~isempty(dist) && ~isa(dist, 'Disabled')
125 dj = dist2json(dist);
136 if isa(node, 'ClassSwitch')
137 csm = node.server.csMatrix;
139 csDict = containers.Map();
141 row = containers.Map();
143 if ri <= size(csm,1) && ci <= size(csm,2) && csm(ri,ci) ~= 0
144 row(
classes{ci}.name) = csm(ri,ci);
148 csDict(
classes{ri}.name) = row;
152 nj(
'classSwitchMatrix') = csDict;
158 if isa(node,
'Cache')
159 cc = containers.Map();
160 cc('items') = node.items.nitems;
161 ilc = node.itemLevelCap;
163 cc('capacity') = ilc;
165 cc('capacity') = ilc(:)';
167 cc('replacement') = repl_to_str(node.replacestrategy);
169 % Hit/miss class mappings
170 hc = full(node.server.hitClass);
171 mc = full(node.server.missClass);
172 if ~isempty(hc) && any(hc > 0)
173 hitMap = containers.Map();
174 for hi = 1:length(hc)
175 if hc(hi) > 0 && hi <= K && hc(hi) <= K
180 cc(
'hitClass') = hitMap;
183 if ~isempty(mc) && any(mc > 0)
184 missMap = containers.Map();
185 for mi = 1:length(mc)
186 if mc(mi) > 0 && mi <= K && mc(mi) <= K
191 cc(
'missClass') = missMap;
195 % Read popularity distributions (setRead)
196 if ~isempty(node.popularity)
197 popMap = containers.Map();
198 for pi = 1:size(node.popularity, 1)
199 for pj = 1:size(node.popularity, 2)
200 if pi <= size(node.popularity, 1) && pj <= size(node.popularity, 2) ...
201 && ~isempty(node.popularity{pi, pj})
202 popDist = node.popularity{pi, pj};
203 dj = dist2json(popDist);
204 if ~isempty(dj) && pj <= K
211 cc(
'popularity') = popMap;
220 if ~isempty(node.output) && isprop(node.output, 'tasksPerLink') && node.output.tasksPerLink > 1
221 nj('tasksPerLink') = node.output.tasksPerLink;
227 if ~isempty(node.joinOf)
228 nj('forkNode') = node.joinOf.name;
232 % DPS scheduling parameters (weights per class)
233 if isa(node, 'Queue') && ~isa(node, 'Delay')
234 sched = node.schedStrategy;
235 if ~isempty(sched) && (sched == SchedStrategy.DPS || sched == SchedStrategy.GPS)
236 sp = containers.Map();
240 w = node.schedStrategyPar(r);
241 if ~isempty(w) && isfinite(w) && w > 0
248 nj(
'schedParams') = sp;
254 if isa(node,
'Transition')
256 nModes = node.getNumberOfModes();
257 allNodes = model.getNodes();
259 mj = containers.Map();
260 if mi <= length(node.modeNames) && ~isempty(node.modeNames{mi})
261 mj('name') = node.modeNames{mi};
263 mj(
'name') = sprintf(
'Mode%d', mi);
266 if mi <= length(node.distributions) && ~isempty(node.distributions{mi})
267 dj = dist2json(node.distributions{mi});
269 mj(
'distribution') = dj;
273 if mi <= length(node.timingStrategies)
274 if node.timingStrategies(mi) == TimingStrategy.TIMED
275 mj('timingStrategy') = 'TIMED';
277 mj('timingStrategy') = 'IMMEDIATE';
281 if mi <= length(node.numberOfServers) && node.numberOfServers(mi) > 1
282 mj('numServers') = node.numberOfServers(mi);
285 if mi <= length(node.firingPriorities) && node.firingPriorities(mi) > 0
286 mj('firingPriority') = node.firingPriorities(mi);
289 if mi <= length(node.firingWeights) && node.firingWeights(mi) ~= 1.0
290 mj('firingWeight') = node.firingWeights(mi);
292 % Enabling conditions
293 if mi <= length(node.enablingConditions)
294 ecMat = node.enablingConditions{mi};
296 for ni = 1:size(ecMat, 1)
297 for ci = 1:size(ecMat, 2)
299 ec = containers.Map();
300 ec('node') = allNodes{ni}.name;
301 ec(
'class') =
classes{ci}.name;
302 ec(
'count') = ecMat(ni, ci);
303 ecList{end+1} = ec; %#ok<AGROW>
308 mj(
'enablingConditions') = ecList;
311 % Inhibiting conditions
312 if mi <= length(node.inhibitingConditions)
313 icMat = node.inhibitingConditions{mi};
315 for ni = 1:size(icMat, 1)
316 for ci = 1:size(icMat, 2)
317 if isfinite(icMat(ni, ci))
318 ic = containers.Map();
319 ic('node') = allNodes{ni}.name;
320 ic(
'class') =
classes{ci}.name;
321 ic(
'count') = icMat(ni, ci);
322 icList{end+1} = ic; %#ok<AGROW>
327 mj(
'inhibitingConditions') = icList;
331 if mi <= length(node.firingOutcomes)
332 foMat = node.firingOutcomes{mi};
334 for ni = 1:size(foMat, 1)
335 for ci = 1:size(foMat, 2)
336 if foMat(ni, ci) ~= 0
337 fo = containers.Map();
338 fo('node') = allNodes{ni}.name;
339 fo(
'class') =
classes{ci}.name;
340 fo(
'count') = foMat(ni, ci);
341 foList{end+1} = fo; %#ok<AGROW>
346 mj(
'firingOutcomes') = foList;
349 modesJson{end+1} = mj; %#ok<AGROW>
351 if ~isempty(modesJson)
352 nj(
'modes') = modesJson;
356 nodesJson{end+1} = nj; %#ok<AGROW>
358result(
'nodes') = nodesJson;
364 cj = containers.Map();
365 cj(
'name') = jc.name;
366 if isa(jc,
'OpenClass')
368 elseif isa(jc, 'ClosedClass')
369 cj('type') = 'Closed';
370 cj('population') = jc.population;
371 if ~isempty(jc.refstat) && isprop(jc.refstat, 'name')
372 cj('refNode') = jc.refstat.name;
378 cj('priority') = jc.priority;
380 classesJson{end+1} = cj; %#ok<AGROW>
382result(
'classes') = classesJson;
385routingMap = containers.Map();
387 sn = model.getStruct();
388 % Prefer rtorig (original
P matrix before ClassSwitch expansion)
389 if ~isempty(sn) && isfield(sn,
'rtorig') && iscell(sn.rtorig) && ~isempty(sn.rtorig) && ~isempty(sn.rtorig{1,1})
391 M_orig = size(P_orig{1,1}, 1);
394 fromTo = containers.Map();
403 ni = sn.nodenames{ii};
404 njn = sn.nodenames{jj};
406 fromTo(ni) = containers.Map();
416 routingMap(key) = fromTo;
420 elseif ~isempty(sn) && isfield(sn,
'rtnodes') && ~isempty(sn.rtnodes)
421 % Fallback to rtnodes
if rtorig not available
426 fromTo = containers.Map();
429 val = rt((ii-1)*K+r, (jj-1)*K+s);
431 ni = sn.nodenames{ii};
432 njn = sn.nodenames{jj};
434 fromTo(ni) = containers.Map();
444 routingMap(key) = fromTo;
450 % If
struct not available, routing stays empty
453routing = containers.Map();
454routing(
'type') =
'matrix';
455routing(
'matrix') = routingMap;
456result(
'routing') = routing;
460% =========================================================================
461% LayeredNetwork serialization
462% =========================================================================
464function result = layered2json(model)
465result = containers.Map();
466result(
'type') =
'LayeredNetwork';
467result(
'name') = model.getName();
472for i = 1:length(hosts)
474 pj = containers.Map();
476 mult = h.multiplicity;
477 if isfinite(mult) && mult > 1
478 pj(
'multiplicity') = mult;
480 schedStr = h.scheduling;
481 if ~isempty(schedStr) && ~strcmpi(schedStr,
'inf')
482 pj('scheduling') = upper(schedStr);
485 if q > 0 && q ~= 0.001
490 pj('speedFactor') = sf;
492 repl = h.replication;
494 pj('replication') = repl;
496 procsJson{end+1} = pj; %#ok<AGROW>
498result(
'processors') = procsJson;
502tasksList = model.tasks;
503for i = 1:length(tasksList)
505 tj = containers.Map();
507 if ~isempty(t.parent)
508 tj('processor') = t.parent.name;
510 mult = t.multiplicity;
511 if isfinite(mult) && mult > 1
512 tj('multiplicity') = mult;
514 schedStr = t.scheduling;
515 if ~isempty(schedStr)
516 tj('scheduling') = upper(schedStr);
519 ttMean = t.thinkTimeMean;
520 if ~isempty(ttMean) && ttMean > GlobalConstants.FineTol
521 if ~isempty(t.thinkTime) && isa(t.thinkTime, 'Distribution')
522 tj('thinkTime') = dist2json(t.thinkTime);
524 params = containers.Map();
525 params('lambda') = 1.0 / ttMean;
526 dj = containers.Map();
528 dj('params') = params;
529 tj('thinkTime') = dj;
533 if ~isempty(t.fanInSource) && ischar(t.fanInSource) && ~isempty(t.fanInSource)
534 fi = containers.Map();
535 fi(t.fanInSource) = t.fanInValue;
539 if ~isempty(t.fanOutDest)
540 fo = containers.Map();
541 for fi_idx = 1:length(t.fanOutDest)
542 fo(t.fanOutDest{fi_idx}) = t.fanOutValue(fi_idx);
546 repl = t.replication;
548 tj(
'replication') = repl;
550 tasksJson{end+1} = tj; %#ok<AGROW>
552result(
'tasks') = tasksJson;
556entriesList = model.entries;
557for i = 1:length(entriesList)
559 ej = containers.Map();
561 if ~isempty(e.parent)
562 ej('task') = e.parent.name;
564 entriesJson{end+1} = ej; %#ok<AGROW>
566result(
'entries') = entriesJson;
568% --- Build reply
map: activityName -> entryName ---
569replyMap = containers.Map();
570for i = 1:length(entriesList)
572 if ~isempty(e.replyActivity)
573 for j = 1:length(e.replyActivity)
574 replyMap(e.replyActivity{j}) = e.name;
581actsList = model.activities;
582for i = 1:length(actsList)
584 aj = containers.Map();
586 if ~isempty(a.parent)
587 if isa(a.parent, 'Task') || isa(a.parent, 'Entry')
588 aj('task') = a.parent.name;
589 elseif ischar(a.parent) || isstring(a.parent)
590 aj('task') =
char(a.parent);
591 elseif ischar(a.parentName) && ~isempty(a.parentName)
592 aj('task') = a.parentName;
594 elseif ~isempty(a.parentName) && ischar(a.parentName)
595 aj('task') = a.parentName;
598 if ~isempty(a.hostDemand) && isa(a.hostDemand, 'Distribution')
599 if ~isa(a.hostDemand, 'Immediate')
600 aj('hostDemand') = dist2json(a.hostDemand);
602 elseif ~isempty(a.hostDemandMean) && a.hostDemandMean > GlobalConstants.FineTol
603 params = containers.Map();
604 params('lambda') = 1.0 / a.hostDemandMean;
605 dj = containers.Map();
607 dj('params') = params;
608 aj('hostDemand') = dj;
611 if ~isempty(a.boundToEntry)
612 aj('boundTo') = a.boundToEntry;
615 if replyMap.isKey(a.name)
616 aj('repliesTo') = replyMap(a.name);
619 if ~isempty(a.syncCallDests)
621 for j = 1:length(a.syncCallDests)
622 sc = containers.Map();
623 sc('entry') = a.syncCallDests{j};
624 if j <= length(a.syncCallMeans) && a.syncCallMeans(j) ~= 1.0
625 sc(
'mean') = a.syncCallMeans(j);
627 synchCalls{end+1} = sc; %#ok<AGROW>
629 aj(
'synchCalls') = synchCalls;
632 if ~isempty(a.asyncCallDests)
634 for j = 1:length(a.asyncCallDests)
635 ac = containers.Map();
636 ac('entry') = a.asyncCallDests{j};
637 if j <= length(a.asyncCallMeans) && a.asyncCallMeans(j) ~= 1.0
638 ac(
'mean') = a.asyncCallMeans(j);
640 asynchCalls{end+1} = ac; %#ok<AGROW>
642 aj(
'asynchCalls') = asynchCalls;
644 actsJson{end+1} = aj; %#ok<AGROW>
646result(
'activities') = actsJson;
650for i = 1:length(tasksList)
652 precs = t.precedences;
653 if isempty(precs),
continue; end
654 for j = 1:length(precs)
656 pj = containers.Map();
660 postType = p.postType;
662 % Determine JSON precedence type and collect activity names
663 if preType == ActivityPrecedenceType.PRE_SEQ && postType == ActivityPrecedenceType.POST_SEQ
664 pj(
'type') =
'Serial';
665 pj(
'activities') = [p.preActs, p.postActs];
666 elseif preType == ActivityPrecedenceType.PRE_SEQ && postType == ActivityPrecedenceType.POST_AND
667 pj(
'type') =
'AndFork';
668 pj(
'activities') = [p.preActs, p.postActs];
669 elseif preType == ActivityPrecedenceType.PRE_AND && postType == ActivityPrecedenceType.POST_SEQ
670 pj(
'type') =
'AndJoin';
671 pj(
'activities') = [p.preActs, p.postActs];
672 elseif preType == ActivityPrecedenceType.PRE_SEQ && postType == ActivityPrecedenceType.POST_OR
673 pj(
'type') =
'OrFork';
674 pj(
'activities') = [p.preActs, p.postActs];
675 if ~isempty(p.postParams)
676 pj('probabilities') = p.postParams(:)';
678 elseif preType == ActivityPrecedenceType.PRE_OR && postType == ActivityPrecedenceType.POST_SEQ
679 pj('type') = 'OrJoin';
680 pj('activities') = [p.preActs, p.postActs];
681 elseif postType == ActivityPrecedenceType.POST_LOOP
683 pj('activities') = [p.preActs, p.postActs];
684 if ~isempty(p.postParams)
685 pj('loopCount') = p.postParams(1);
690 precsJson{end+1} = pj; %#ok<AGROW>
693if ~isempty(precsJson)
694 result(
'precedences') = precsJson;
699% =========================================================================
700% Distribution serialization
701% =========================================================================
703function d = dist2json(dist)
704% Convert a Distribution to a containers.Map
for JSON output.
710cn = builtin(
'class', dist);
713 d(
'type') =
'Disabled';
715 d(
'type') =
'Immediate';
718 params = containers.Map();
719 params(
'lambda') = dist.getParam(1).paramValue;
720 d(
'params') = params;
723 params = containers.Map();
724 params(
'value') = dist.getParam(1).paramValue;
725 d(
'params') = params;
727 d(
'type') =
'Erlang';
728 params = containers.Map();
729 params(
'lambda') = dist.getParam(1).paramValue;
730 params(
'k') = dist.getParam(2).paramValue;
731 d(
'params') = params;
733 d(
'type') =
'HyperExp';
734 params = containers.Map();
735 p = dist.getParam(1).paramValue;
736 l1 = dist.getParam(2).paramValue;
737 l2 = dist.getParam(3).paramValue;
739 params(
'p') = [p, 1-p];
740 params(
'lambda') = [l1, l2];
743 params('lambda
') = [l1, l2];
745 d('params
') = params;
748 params = containers.Map();
749 params('alpha
') = dist.getParam(1).paramValue;
750 params('beta
') = dist.getParam(2).paramValue;
751 d('params
') = params;
753 d('type
') = 'Lognormal
';
754 params = containers.Map();
755 params('mu
') = dist.getParam(1).paramValue;
756 params('sigma
') = dist.getParam(2).paramValue;
757 d('params
') = params;
759 d('type
') = 'Uniform
';
760 params = containers.Map();
761 params('a
') = dist.getParam(1).paramValue;
762 params('b
') = dist.getParam(2).paramValue;
763 d('params
') = params;
766 params = containers.Map();
767 params('s
') = dist.getParam(1).paramValue;
768 params('n
') = dist.getParam(2).paramValue;
769 d('params
') = params;
771 d('type
') = 'Pareto
';
772 params = containers.Map();
773 params('alpha
') = dist.getParam(1).paramValue;
774 params('scale
') = dist.getParam(2).paramValue;
775 d('params
') = params;
776 case {'PH
', 'APH
', 'Coxian
', 'Cox2
'}
778 ph = containers.Map();
779 alpha = dist.getParam(1).paramValue;
780 T = dist.getParam(2).paramValue;
782 ph('alpha
') = alpha(:)';
790 mapSpec = containers.Map();
791 mapSpec(
'D0') = dist.getParam(1).paramValue;
792 mapSpec(
'D1') = dist.getParam(2).paramValue;
795 % MMPP2 params are (lambda0, lambda1, sigma0, sigma1), not D0/D1.
796 % Use the process representation to get the actual D0/D1 matrices.
798 mapSpec = containers.Map();
799 proc = dist.getProcess();
800 mapSpec(
'D0') = full(proc{1});
801 mapSpec(
'D1') = full(proc{2});
803 case 'DiscreteSampler'
804 d(
'type') =
'DiscreteSampler';
805 params = containers.Map();
806 params(
'p') = dist.getParam(1).paramValue(:)
';
807 params('x
') = dist.getParam(2).paramValue(:)';
808 d(
'params') = params;
810 % Fallback: fit from mean
814 fit = containers.Map();
815 fit(
'method') =
'fitMean';
825% =========================================================================
827% =========================================================================
829function s = encode_value(val, indent)
830% Recursively encode a MATLAB value to JSON
string.
831if nargin < 2, indent = 0; end
832pad = repmat(
' ', 1, indent);
833pad2 = repmat(
' ', 1, indent + 2);
835if isa(val,
'containers.Map')
840 parts = cell(1, length(ks));
844 parts{i} = sprintf('%s
"%s": %s
', pad2, json_escape(k), encode_value(v, indent + 2));
846 s = sprintf('{\n%s\n%s}
', strjoin(parts, sprintf(',\n
')), pad);
848elseif ischar(val) || isstring(val)
849 s = sprintf('"%s"', json_escape(char(val)));
850elseif islogical(val) && isscalar(val)
851 if val, s = 'true'; else, s = 'false'; end
852elseif isnumeric(val) && isscalar(val)
856 if val > 0, s = '"Infinity"'; else, s = '"-Infinity"'; end
857 elseif val == floor(val) && abs(val) < 1e15
858 s = sprintf('%d
', val);
860 s = sprintf('%.15g
', val);
862elseif isnumeric(val) && isvector(val) && ~isscalar(val)
863 parts = cell(1, length(val));
864 for i = 1:length(val)
865 parts{i} = encode_value(val(i), 0);
867 s = ['[
', strjoin(parts, ',
'), ']
'];
868elseif isnumeric(val) && ismatrix(val) && ~isvector(val)
869 rows = cell(1, size(val, 1));
870 for i = 1:size(val, 1)
871 rows{i} = encode_value(val(i,:), 0);
873 s = ['[
', strjoin(rows, ',
'), ']
'];
878 parts = cell(1, length(val));
879 for i = 1:length(val)
880 parts{i} = sprintf('%s%s
', pad2, encode_value(val{i}, indent + 2));
882 s = sprintf('[\n%s\n%s]
', strjoin(parts, sprintf(',\n
')), pad);
884elseif isstruct(val) && isscalar(val)
885 fnames = fieldnames(val);
889 parts = cell(1, length(fnames));
890 for i = 1:length(fnames)
893 parts{i} = sprintf('%s
"%s": %s
', pad2, json_escape(fn), encode_value(fv, indent + 2));
895 s = sprintf('{\n%s\n%s}
', strjoin(parts, sprintf(',\n
')), pad);
902function s = json_escape(str)
903% Escape special characters for JSON strings.
904s = strrep(str, '\
', '\\
');
905s = strrep(s, '"', '\"');
906s = strrep(s, sprintf('\n'), '\n');
907s = strrep(s, sprintf('\r'), '\r');
908s = strrep(s, sprintf('\t'), '\t');
912% =========================================================================
914% =========================================================================
916function s = node_type_str(node)
917% Get the JSON node type string for a node object.
918if isa(node, 'Source'), s = 'Source';
919elseif isa(node, 'Sink'), s = 'Sink';
920elseif isa(node, 'Delay'), s = 'Delay';
921elseif isa(node, 'Cache'), s = 'Cache';
922elseif isa(node, 'Place'), s = 'Place';
923elseif isa(node, 'Transition'), s = 'Transition';
924elseif isa(node, 'Queue'), s = 'Queue';
925elseif isa(node, 'Fork'), s = 'Fork';
926elseif isa(node, 'Join'), s = 'Join';
927elseif isa(node, 'Router'), s = 'Router';
928elseif isa(node, 'ClassSwitch'), s = 'ClassSwitch';
933function s = sched_id_to_str(id)
934% Map SchedStrategy numeric ID to schema-compatible string.
935if id == SchedStrategy.INF, s = 'INF';
936elseif id == SchedStrategy.FCFS, s = 'FCFS';
937elseif id == SchedStrategy.LCFS, s = 'LCFS';
938elseif id == SchedStrategy.LCFSPR, s = 'LCFSPR';
939elseif id == SchedStrategy.PS, s = 'PS';
940elseif id == SchedStrategy.DPS, s = 'DPS';
941elseif id == SchedStrategy.GPS, s = 'GPS';
942elseif id == SchedStrategy.SIRO, s = 'SIRO';
943elseif id == SchedStrategy.SJF, s = 'SJF';
944elseif id == SchedStrategy.LJF, s = 'LJF';
945elseif id == SchedStrategy.SEPT, s = 'SEPT';
946elseif id == SchedStrategy.LEPT, s = 'LEPT';
947elseif id == SchedStrategy.HOL, s = 'HOL';
948elseif id == SchedStrategy.FORK, s = 'FORK';
949elseif id == SchedStrategy.EXT, s = 'EXT';
950elseif id == SchedStrategy.REF, s = 'REF';
951elseif id == SchedStrategy.POLLING, s = 'POLLING';
952elseif id == SchedStrategy.PSPRIO, s = 'PSPRIO';
953elseif id == SchedStrategy.DPSPRIO, s = 'DPSPRIO';
954elseif id == SchedStrategy.GPSPRIO, s = 'GPSPRIO';
955elseif id == SchedStrategy.FCFSPRIO, s = 'FCFSPRIO';
960function s = repl_to_str(id)
961% Map ReplacementStrategy numeric ID to schema string.
962if id == ReplacementStrategy.LRU, s = 'LRU';
963elseif id == ReplacementStrategy.FIFO, s = 'FIFO';
964elseif id == ReplacementStrategy.RR, s = 'RR';
965elseif id == ReplacementStrategy.SFIFO, s = 'SFIFO';