LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
LQN2SCRIPT.m
1function LQN2SCRIPT(lqnmodel, modelName, fid)
2% LQN2SCRIPT(MODEL, MODELNAME, FID)
3
4% Copyright (c) 2012-2026, Imperial College London
5% All rights reserved.
6
7if nargin<2%~exist('modelName','var')
8 modelName='myLayeredModel';
9end
10if nargin<3%~exist('fid','var')
11 fid=1;
12end
13if ischar(fid)
14 fid = fopen(fid,'w');
15end
16sn = lqnmodel.getStruct;
17
18%% initialization
19fprintf(fid,'model = LayeredNetwork(''%s'');\n',modelName);
20fprintf(fid,'\n');
21%% host processors
22for h=1:sn.nhosts
23 fprintf(fid, 'P{%d} = Processor(model, ''%s'', %d, %s);\n', h, sn.names{h}, sn.mult(h), strrep(SchedStrategy.toFeature(SchedStrategy.fromText(sn.sched(h))),'_','.'));
24 if sn.repl(h)~=1
25 fprintf(fid, 'P{%d}.setReplication(%d);\n', h, sn.repl(h));
26 end
27end
28fprintf(fid,'\n');
29%% tasks
30for t=1:sn.ntasks
31 tidx = sn.tshift+t;
32 fprintf(fid, 'T{%d} = Task(model, ''%s'', %d, %s).on(P{%d})', t, sn.names{tidx}, sn.mult(tidx), strrep(SchedStrategy.toFeature(SchedStrategy.fromText(sn.sched(tidx))),'_','.'),sn.parent(tidx));
33 if sn.repl(tidx)~=1
34 fprintf(fid, 'T{%d}.setReplication', t, sn.repl(tidx));
35 end
36 if ~isempty(sn.think{tidx})
37 switch sn.think{tidx}.name
38 case 'Immediate'
39 fprintf(fid, '.setThinkTime(Immediate())');
40 case 'Exp'
41 fprintf(fid, '.setThinkTime(Exp.fitMean(%g))', sn.think{tidx}.getMean);
42 case {'Erlang','HyperExp', 'Coxian', 'APH'}
43 fprintf(fid, '.setThinkTime(%s.fitMeanAndSCV(%g,%g))', sn.think{tidx}.name, sn.think{tidx}.getMean, sn.think{tidx}.getSCV);
44 otherwise
45 line_error(mfilename,sprintf('LQN2SCRIPT does not support the %d distribution yet.',sn.think{tidx}.name));
46 end
47 end
48 fprintf(fid,';\n');
49end
50fprintf(fid,'\n');
51%% entries
52for e=1:sn.nentries
53 eidx = sn.eshift+e;
54 fprintf(fid, 'E{%d} = Entry(model, ''%s'').on(T{%d});\n', e, sn.names{eidx},sn.parent(eidx)-sn.tshift);
55end
56fprintf(fid,'\n');
57%% activities
58for a=1:sn.nacts
59 aidx = sn.ashift+a;
60 tidx = sn.parent(aidx);
61 onTask = tidx-sn.tshift;
62 boundTo = find(sn.graph((sn.eshift+1):(sn.eshift+sn.nentries),aidx));
63 boundToStr = '';
64 if ~isempty(boundTo)
65 boundToStr = sprintf('.boundTo(E{%d})',boundTo);
66 end
67 if sn.sched(tidx) ~= SchedStrategy.REF % ref tasks don't reply
68 repliesToStr = '';
69 repliesTo = find(sn.replygraph(a,:)); % index of entry
70 if ~isempty(repliesTo)
71 if ~sn.isref(sn.parent(sn.eshift+repliesTo))
72 repliesToStr = sprintf('.repliesTo(E{%d})',repliesTo);
73 end
74 end
75 end
76 callStr = '';
77 if ~isempty(sn.callpair)
78 cidxs = find(sn.callpair(:,1)==aidx);
79 calls = sn.callpair(:,2);
80 for c=cidxs(:)'
81 switch sn.calltype(c)
82 case CallType.SYNC
83 callStr = sprintf('%s.synchCall(E{%d},%g)',callStr,calls(c)-sn.eshift,sn.callproc{c}.getMean);
84 case CallType.ASYNC
85 callStr = sprintf('%s.asynchCall(E{%d},%g)',callStr,calls(c)-sn.eshift,sn.callproc{c}.getMean);
86 end
87 end
88 end
89 switch sn.hostdem{aidx}.name
90 case 'Immediate'
91 fprintf(fid, 'A{%d} = Activity(model, ''%s'', Immediate()).on(T{%d})%s%s%s;\n', a, sn.names{aidx}, onTask, boundToStr, callStr, repliesToStr);
92 case 'Exp'
93 fprintf(fid, 'A{%d} = Activity(model, ''%s'', Exp.fitMean(%g)).on(T{%d})%s%s%s;\n', a, sn.names{aidx},sn.hostdem{aidx}.getMean, onTask, boundToStr, callStr, repliesToStr);
94 case {'Erlang','HyperExp','Coxian','APH'}
95 fprintf(fid, 'A{%d} = Activity(model, ''%s'', %s.fitMeanAndSCV(%g,%g)).on(T{%d})%s%s%s;\n', a, sn.names{aidx},sn.hostdem{aidx}.name,sn.hostdem{aidx}.getMean,sn.hostdem{aidx}.getSCV, onTask, boundToStr, callStr, repliesToStr);
96 otherwise
97 line_error(mfilename,sprintf('LQN2SCRIPT does not support the %d distribution yet.',sn.hostdem{aidx}.name));
98 end
99end
100fprintf(fid,'\n');
101%% think times
102for h=1:sn.nhosts
103 if ~isempty(sn.think{h})
104 switch sn.think{h}.name
105 case 'Immediate'
106 fprintf(fid, 'P{%d}.setThinkTime(Immediate());\n', h);
107 case 'Exp'
108 fprintf(fid, 'P{%d}.setThinkTime(Exp.fitMean(%g));\n', h, sn.think{h}.getMean);
109 case {'Erlang','HyperExp','Coxian','APH'}
110 fprintf(fid, 'P{%d}.setThinkTime(%s.fitMeanAndSCV(%g,%g));\n', h, sn.think{h}.name, sn.think{h}.getMean, sn.think{h}.getSCV);
111 otherwise
112 line_error(mfilename,sprintf('LQN2SCRIPT does not support the %d distribution yet.',sn.think{h}.name));
113 end
114 end
115end
116
117%% Sequential precedences
118for ai = 1:sn.nacts
119 aidx = sn.ashift + ai;
120 tidx = sn.parent(aidx);
121 % for all successors
122 for bidx=find(sn.graph(aidx,:))
123 if bidx > sn.ashift % ignore precedence between entries and activities
124 % Serial pattern (SEQ)
125 if full(sn.actpretype(aidx)) == ActivityPrecedenceType.PRE_SEQ && full(sn.actposttype(bidx)) == ActivityPrecedenceType.POST_SEQ
126 fprintf(fid, 'T{%d}.addPrecedence(ActivityPrecedence.Serial(A{%d}, A{%d}));\n', tidx-sn.tshift, aidx-sn.ashift, bidx-sn.ashift);
127 end
128 end
129 end
130end
131
132%% Loop precedences (POST_LOOP)
133% Loop structure in sn.graph:
134% - Entry activity (preAct) has edge to first loop body activity
135% - Last loop body activity has back-edge to first loop body (weight = 1-1/counts)
136% - Last loop body activity has edge to end activity (weight = 1/counts)
137% - All loop body and end activities have POST_LOOP type
138processedLoops = false(1, sn.nacts);
139for ai = 1:sn.nacts
140 aidx = sn.ashift + ai;
141 tidx = sn.parent(aidx);
142 % Check if this activity starts a loop (has a successor with POST_LOOP type)
143 % and hasn't been processed as part of another loop
144 if processedLoops(ai)
145 continue;
146 end
147
148 successors = find(sn.graph(aidx,:));
149 for bidx = successors
150 if bidx > sn.ashift && full(sn.actposttype(bidx)) == ActivityPrecedenceType.POST_LOOP
151 % Skip if this loop body activity was already processed
152 if processedLoops(bidx - sn.ashift)
153 continue;
154 end
155 % Found start of a loop: aidx is the entry, bidx is first loop body activity
156 loopStart = bidx;
157 precMarker = ai;
158 loopActs = {};
159
160 % Follow the chain of POST_LOOP activities
161 curIdx = loopStart;
162 while true
163 loopActs{end+1} = curIdx - sn.ashift; %#ok<AGROW>
164 processedLoops(curIdx - sn.ashift) = true;
165
166 % Find successors of current activity
167 curSuccessors = find(sn.graph(curIdx,:));
168 curSuccessors = curSuccessors(curSuccessors > sn.ashift);
169
170 % Check for loop termination: find the end activity
171 % End activity has weight = 1/counts (not the back-edge weight)
172 endIdx = 0;
173 nextIdx = 0;
174 for succIdx = curSuccessors
175 if full(sn.actposttype(succIdx)) == ActivityPrecedenceType.POST_LOOP
176 if succIdx == loopStart
177 % This is the back-edge, skip it
178 continue;
179 end
180 weight = full(sn.graph(curIdx, succIdx));
181 if weight > 0 && weight < 1
182 % This is the end activity (weight = 1/counts)
183 endIdx = succIdx;
184 else
185 % This is the next activity in the loop body (weight = 1.0)
186 nextIdx = succIdx;
187 end
188 end
189 end
190
191 if endIdx > 0
192 % Found end activity - calculate counts and output
193 weight = full(sn.graph(curIdx, endIdx));
194 if weight > 0
195 counts = 1/weight;
196 else
197 counts = 1; % Fallback to prevent division by zero
198 end
199 loopActs{end+1} = endIdx - sn.ashift; %#ok<AGROW>
200 processedLoops(endIdx - sn.ashift) = true;
201
202 % Build the activity list string
203 precActs = sprintf('A{%d}', loopActs{1});
204 for k = 2:length(loopActs)
205 precActs = sprintf('%s, A{%d}', precActs, loopActs{k});
206 end
207 fprintf(fid, 'T{%d}.addPrecedence(ActivityPrecedence.Loop(A{%d}, {%s}, %g));\n', tidx-sn.tshift, precMarker, precActs, counts);
208 break;
209 elseif nextIdx > 0
210 % Continue to next activity in loop body
211 curIdx = nextIdx;
212 else
213 % No more successors - shouldn't happen in valid loop
214 break;
215 end
216 end
217 break; % Only process one loop starting from this activity
218 end
219 end
220end
221
222%% OrFork precedences (POST_OR)
223precMarker = 0;
224for ai = 1:sn.nacts
225 aidx = sn.ashift + ai;
226 tidx = sn.parent(aidx);
227 % for all successors
228 for bidx=find(sn.graph(aidx,:))
229 if bidx > sn.ashift % ignore precedence between entries and activities
230 % Or pattern (POST_OR)
231 if full(sn.actposttype(bidx)) == ActivityPrecedenceType.POST_OR
232 if precMarker == 0 % start a new orjoin
233 precMarker = aidx-sn.ashift;
234 precActs = sprintf('A{%d}', bidx-sn.ashift);
235 probs = sprintf('%g',full(sn.graph(aidx,bidx)));
236 else
237 precActs = sprintf('%s, A{%d}', precActs, bidx-sn.ashift);
238 probs = sprintf('%s,%g',probs,full(sn.graph(aidx,bidx)));
239 end
240 end
241 end
242 end
243 if precMarker > 0
244 fprintf(fid, 'T{%d}.addPrecedence(ActivityPrecedence.OrFork(A{%d},{%s},[%s]));\n', tidx-sn.tshift, precMarker, precActs, probs);
245 precMarker = 0;
246 end
247end
248
249%% AndFork precedences (POST_AND)
250precMarker = 0;
251for ai = 1:sn.nacts
252 aidx = sn.ashift + ai;
253 tidx = sn.parent(aidx);
254 % for all successors
255 for bidx=find(sn.graph(aidx,:))
256 if bidx > sn.ashift % ignore precedence between entries and activities
257 % Or pattern (POST_AND)
258 if full(sn.actposttype(bidx)) == ActivityPrecedenceType.POST_AND
259 if precMarker == 0 % start a new orjoin
260 precMarker = aidx-sn.ashift;
261 precActs = sprintf('A{%d}', bidx-sn.ashift);
262 else
263 precActs = sprintf('%s, A{%d}', precActs, bidx-sn.ashift);
264 end
265 end
266 end
267 end
268 if precMarker > 0
269 fprintf(fid, 'T{%d}.addPrecedence(ActivityPrecedence.AndFork(A{%d},{%s}));\n', tidx-sn.tshift, precMarker, precActs);
270 precMarker = 0;
271 end
272end
273
274
275%% OrJoin precedences (PRE_OR)
276precMarker = 0;
277for bi = sn.nacts:-1:1
278 bidx = sn.ashift + bi;
279 tidx = sn.parent(bidx);
280 % for all predecessors
281 for aidx=find(sn.graph(:,bidx))'
282 if aidx > sn.ashift % ignore precedence between entries and activities
283 % OrJoin pattern (PRE_OR)
284 if full(sn.actpretype(aidx)) == ActivityPrecedenceType.PRE_OR
285 if precMarker == 0 % start a new orjoin
286 precMarker = bidx-sn.ashift;
287 precActs = sprintf('A{%d}', aidx-sn.ashift);
288 else
289 precActs = sprintf('%s, A{%d}',precActs, aidx-sn.ashift);
290 end
291 end
292 end
293 end
294 if precMarker > 0
295 fprintf(fid, 'T{%d}.addPrecedence(ActivityPrecedence.OrJoin({%s}, A{%d}));\n', tidx-sn.tshift, precActs, precMarker);
296 precMarker = 0;
297 end
298end
299
300%% OrJoin precedences (PRE_AND)
301precMarker = 0;
302for bi = sn.nacts:-1:1
303 bidx = sn.ashift + bi;
304 tidx = sn.parent(bidx);
305 % for all predecessors
306 for aidx=find(sn.graph(:,bidx))'
307 if aidx > sn.ashift % ignore precedence between entries and activities
308 % OrJoin pattern (PRE_AND)
309 if full(sn.actpretype(aidx)) == ActivityPrecedenceType.PRE_AND
310 if precMarker == 0 % start a new orjoin
311 precMarker = bidx-sn.ashift;
312 precActs = sprintf('A{%d}', aidx-sn.ashift);
313 else
314 precActs = sprintf('%s, A{%d}',precActs, aidx-sn.ashift);
315 end
316 end
317 end
318 end
319 if precMarker > 0
320 fprintf(fid, 'T{%d}.addPrecedence(ActivityPrecedence.AndJoin({%s}, A{%d}));\n', tidx-sn.tshift, precActs, precMarker);
321 precMarker = 0;
322 end
323end
324
325if ischar(fid)
326 fclose(fid);
327end
328end