LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
getStruct.m
1function lsn = getStruct(self, regenerate)
2% LSN = GETSTRUCT(SELF, regenerate)
3%
4%
5% Copyright 2012-2026, Imperial College London
6if nargin<2
7 regenerate = false;
8end
9if ~isempty(self.lsn) && ~regenerate
10 lsn = self.lsn;
11 return
12end
13lsn = LayeredNetworkStruct();
14lsn.nidx = 0; % total number of hosts, tasks, entries, and activities, except the reference tasks
15lsn.hshift = 0;
16lsn.nhosts = length(self.hosts);
17lsn.ntasks = length(self.tasks);
18lsn.nentries = length(self.entries);
19lsn.nacts = length(self.activities);
20lsn.tshift = lsn.nhosts;
21lsn.eshift = lsn.nhosts + lsn.ntasks;
22lsn.ashift = lsn.nhosts + lsn.ntasks + lsn.nentries;
23lsn.cshift = lsn.nhosts + lsn.ntasks + lsn.nentries + lsn.nacts;
24
25%% analyze static properties
26lsn.nidx = lsn.nhosts + lsn.ntasks + lsn.nentries + lsn.nacts;
27idx = 1;
28lsn.tasksof = cell(lsn.nhosts,1);
29lsn.entriesof = cell(lsn.nhosts+lsn.ntasks,1);
30lsn.actsof = cell(lsn.nhosts+lsn.ntasks+lsn.nentries,1);
31lsn.callsof = cell(lsn.nacts,1);
32lsn.hostdem = {};
33lsn.actthink = {};
34lsn.think = {};
35lsn.sched = [];
36lsn.names = {};
37lsn.hashnames = {};
38%lsn.shortnames = {};
39lsn.mult = zeros(lsn.nhosts+lsn.ntasks,1);
40lsn.maxmult = zeros(lsn.nhosts+lsn.ntasks,1);
41lsn.repl = zeros(lsn.nhosts+lsn.ntasks,1);
42lsn.type = zeros(lsn.nidx,1);
43lsn.graph = zeros(lsn.nidx,lsn.nidx);
44loop_back_edges = false(lsn.nidx,lsn.nidx);
45%lsn.replies = [];
46lsn.replygraph = false(lsn.nacts,lsn.nentries);
47
48lsn.nitems = zeros(lsn.nhosts+lsn.ntasks+lsn.nentries,1);
49lsn.itemcap = {};
50lsn.itemproc = {};
51lsn.iscache = zeros(lsn.nhosts+lsn.ntasks,1);
52lsn.setuptime = cell(lsn.nhosts+lsn.ntasks,1);
53lsn.delayofftime = {};
54lsn.isfunction = zeros(lsn.nhosts+lsn.ntasks,1);
55
56lsn.parent = [];
57for p=1:lsn.nhosts % for every processor, scheduling, multiplicity, replication, names, type
58 lsn.sched(idx,1) = SchedStrategy.fromText(self.hosts{p}.scheduling);
59 lsn.mult(idx,1) = self.hosts{p}.multiplicity;
60 lsn.repl(idx,1) = self.hosts{p}.replication;
61 lsn.names{idx,1} = self.hosts{p}.name;
62 lsn.hashnames{idx,1} = ['P:',lsn.names{idx,1}];
63 %lsn.shortnames{idx,1} = ['P',num2str(p)];
64 lsn.type(idx,1) = LayeredNetworkElement.HOST; % processor
65 idx = idx + 1;
66end
67
68for t=1:lsn.ntasks
69 lsn.sched(idx,1) = SchedStrategy.fromText(self.tasks{t}.scheduling);
70 lsn.hostdem{idx,1} = Immediate.getInstance();
71 lsn.think{idx,1} = self.tasks{t}.thinkTime;
72 lsn.mult(idx,1) = self.tasks{t}.multiplicity;
73 lsn.repl(idx,1) = self.tasks{t}.replication;
74 lsn.names{idx,1} = self.tasks{t}.name;
75 switch lsn.sched(idx,1)
76 case SchedStrategy.REF
77 lsn.hashnames{idx,1} = ['R:',lsn.names{idx,1}];
78 %lsn.shortnames{idx,1} = ['R',num2str(idx-tshift)];
79 otherwise
80 lsn.hashnames{idx,1} = ['T:',lsn.names{idx,1}];
81 %lsn.shortnames{idx,1} = ['T',num2str(idx-tshift)];
82 end
83 switch class(self.tasks{t})
84 case 'CacheTask'
85 lsn.nitems(idx,1) = self.tasks{t}.items;
86 lsn.itemcap{idx,1} = self.tasks{t}.itemLevelCap;
87 lsn.replacestrat(idx,1) = self.tasks{t}.replacestrategy;
88 lsn.hashnames{idx,1} = ['C:',lsn.names{idx,1}];
89 case 'FunctionTask'
90 lsn.setuptime{idx,1} = self.tasks{t}.SetupTime;
91 lsn.delayofftime{idx,1} = self.tasks{t}.DelayOffTime;
92 lsn.hashnames{idx,1} = ['F:',lsn.names{idx,1}];
93 %lsn.shortnames{idx,1} = ['C',num2str(idx-tshift)];
94 end
95 pidx = find(cellfun(@(x) strcmp(x.name, self.tasks{t}.parent.name), self.hosts));
96 lsn.parent(idx) = pidx;
97 lsn.graph(idx, pidx) = 1;
98 lsn.type(idx) = LayeredNetworkElement.TASK; % task
99 idx = idx + 1;
100end
101
102for p=1:lsn.nhosts % for every processor
103 pidx = p;
104 lsn.tasksof{pidx} = find(lsn.parent == pidx);
105end
106
107for e=1:lsn.nentries
108 lsn.names{idx,1} = self.entries{e}.name;
109
110 % Extract open arrival distribution if present
111 if ~isempty(self.entries{e}.arrival) && isa(self.entries{e}.arrival, 'Distribution')
112 lsn.arrival{idx,1} = self.entries{e}.arrival;
113 end
114
115 switch class(self.entries{e})
116 case 'Entry'
117 lsn.hashnames{idx,1} = ['E:',lsn.names{idx,1}];
118 for a=1:length(self.entries{e}.replyActivity)
119 ractname = self.entries{e}.replyActivity{a};
120 ractidx = find(cellfun(@(x) strcmp(x.name, ractname), self.activities));
121 lsn.replygraph(ractidx,e)=true;
122 end
123 %lsn.shortnames{idx,1} = ['E',num2str(idx-eshift)];
124 case 'ItemEntry'
125 lsn.hashnames{idx,1} = ['I:',lsn.names{idx,1}];
126 %lsn.shortnames{idx,1} = ['I',num2str(idx-eshift)];
127 lsn.nitems(idx,1) = self.entries{e}.cardinality;
128 lsn.itemproc{idx,1} = self.entries{e}.popularity;
129 end
130 lsn.hostdem{idx,1} = Immediate.getInstance();
131 tidx = lsn.nhosts + find(cellfun(@(x) strcmp(x.name, self.entries{e}.parent.name), self.tasks));
132 lsn.parent(idx) = tidx;
133 lsn.graph(tidx,idx) = 1;
134 lsn.entriesof{tidx}(end+1) = idx;
135 lsn.type(idx) = LayeredNetworkElement.ENTRY; % entries
136 idx = idx + 1;
137end
138
139for a=1:lsn.nacts
140 lsn.names{idx,1} = self.activities{a}.name;
141 lsn.hashnames{idx,1} = ['A:',lsn.names{idx,1}];
142 %lsn.shortnames{idx,1} = ['A',num2str(idx - lsn.ashift)];
143 lsn.hostdem{idx,1} = self.activities{a}.hostDemand;
144 lsn.actthink{idx,1} = self.activities{a}.thinkTime;
145 tidx = lsn.nhosts + find(cellfun(@(x) strcmp(x.name, self.activities{a}.parent.name), self.tasks));
146 lsn.parent(idx) = tidx;
147 lsn.actsof{tidx}(end+1) = idx;
148 lsn.type(idx) = LayeredNetworkElement.ACTIVITY; % activities
149 idx = idx + 1;
150end
151
152nidx = idx - 1; % number of indices
153lsn.graph(nidx,nidx) = 0;
154
155tasks = self.tasks;
156%% now analyze calls
157cidx = 0;
158lsn.calltype = sparse([],lsn.nidx,1);
159lsn.iscaller = sparse(lsn.nidx,lsn.nidx);
160lsn.issynccaller = sparse(lsn.nidx,lsn.nidx);
161lsn.isasynccaller = sparse(lsn.nidx,lsn.nidx);
162lsn.callpair = [];
163lsn.callproc = {};
164lsn.callnames = {};
165lsn.callhashnames = {};
166%lsn.callshortnames = {};
167lsn.taskgraph = sparse(lsn.tshift+lsn.ntasks, lsn.tshift+lsn.ntasks);
168lsn.actpretype = sparse(lsn.nidx,1);
169lsn.actposttype = sparse(lsn.nidx,1);
170
171% Track boundToEntry mappings to validate uniqueness
172boundEntries = {};
173boundActivities = {};
174
175for t = 1:lsn.ntasks
176 tidx = lsn.tshift+t;
177 for a=1:length(self.tasks{t}.activities)
178 aidx = findstring(lsn.hashnames, ['A:',tasks{t}.activities(a).name]);
179 lsn.callsof{aidx} = [];
180 boundToEntry = tasks{t}.activities(a).boundToEntry;
181 %for b=1:length(boundToEntry)
182 eidx = findstring(lsn.hashnames, ['E:',boundToEntry]);
183 if eidx<0
184 eidx = findstring(lsn.hashnames, ['I:',boundToEntry]);
185 end
186 if eidx>0
187 lsn.graph(eidx, aidx) = 1;
188
189 % Check if this entry is already bound to another activity
190 activityName = tasks{t}.activities(a).name;
191 existingIdx = find(strcmp(boundEntries, boundToEntry), 1);
192 if ~isempty(existingIdx)
193 line_error(mfilename, sprintf('Multiple activities (%s, %s) are bound to the same entry: %s', ...
194 boundActivities{existingIdx}, activityName, boundToEntry));
195 else
196 boundEntries{end+1} = boundToEntry;
197 boundActivities{end+1} = activityName;
198 end
199 end
200 %end
201
202 for s=1:length(tasks{t}.activities(a).syncCallDests)
203 target_eidx = findstring(lsn.hashnames, ['E:',tasks{t}.activities(a).syncCallDests{s}]);
204 if target_eidx < 0
205 target_eidx = findstring(lsn.hashnames, ['I:',tasks{t}.activities(a).syncCallDests{s}]);
206 end
207 target_tidx = lsn.parent(target_eidx);
208 cidx = cidx + 1;
209 lsn.calltype(cidx,1) = CallType.SYNC;
210 lsn.callpair(cidx,1:2) = [aidx,target_eidx];
211 if tidx == target_tidx
212 line_error(mfilename, 'An entry on a task cannot call another entry on the same task.');
213 end
214 lsn.callnames{cidx,1} = [lsn.names{aidx},'=>',lsn.names{target_eidx}];
215 lsn.callhashnames{cidx,1} = [lsn.hashnames{aidx},'=>',lsn.hashnames{target_eidx}];
216 %lsn.callshortnames{cidx,1} = [lsn.shortnames{aidx},'=>',lsn.shortnames{target_eidx}];
217 lsn.callproc{cidx,1} = Geometric(1/tasks{t}.activities(a).syncCallMeans(s)); % synch
218 lsn.callsof{aidx}(end+1) = cidx;
219 lsn.iscaller(tidx, target_tidx) = true;
220 lsn.iscaller(aidx, target_tidx) = true;
221 lsn.iscaller(tidx, target_eidx) = true;
222 lsn.iscaller(aidx, target_eidx) = true;
223 lsn.issynccaller(tidx, target_tidx) = true;
224 lsn.issynccaller(aidx, target_tidx) = true;
225 lsn.issynccaller(tidx, target_eidx) = true;
226 lsn.issynccaller(aidx, target_eidx) = true;
227 lsn.taskgraph(tidx, target_tidx) = 1;
228 lsn.graph(aidx, target_eidx) = 1;
229 end
230
231 for s=1:length(tasks{t}.activities(a).asyncCallDests)
232 target_entry_name = tasks{t}.activities(a).asyncCallDests{s};
233 target_eidx = findstring(lsn.hashnames,['E:',target_entry_name]);
234 if target_eidx < 0
235 target_eidx = findstring(lsn.hashnames, ['I:',target_entry_name]);
236 end
237 % Validate that the target entry exists
238 if target_eidx <= 0
239 line_error(mfilename, sprintf('Activity "%s" has an async call to non-existent entry "%s".', tasks{t}.activities(a).name, target_entry_name));
240 end
241 target_tidx = lsn.parent(target_eidx);
242 % Check for self-referential async calls (task calling itself)
243 if tidx == target_tidx
244 line_error(mfilename, sprintf('Activity "%s" in task "%s" has an async call to an entry on the same task. Async self-calls are not supported.', tasks{t}.activities(a).name, tasks{t}.name));
245 end
246 cidx = cidx + 1;
247 lsn.callpair(cidx,1:2) = [aidx,target_eidx];
248 lsn.calltype(cidx,1) = CallType.ASYNC; % async
249 lsn.callnames{cidx,1} = [lsn.names{aidx},'->',lsn.names{target_eidx}];
250 lsn.callhashnames{cidx,1} = [lsn.hashnames{aidx},'->',lsn.hashnames{target_eidx}];
251 %lsn.callshortnames{cidx,1} = [lsn.shortnames{aidx},'->',lsn.shortnames{target_eidx}];
252 lsn.callproc{cidx,1} = Geometric(1/tasks{t}.activities(a).asyncCallMeans(s)); % asynch
253 lsn.callsof{aidx}(end+1) = cidx;
254 lsn.iscaller(aidx, target_tidx) = true;
255 lsn.iscaller(aidx, target_eidx) = true;
256 lsn.iscaller(tidx, target_tidx) = true;
257 lsn.iscaller(tidx, target_eidx) = true;
258 lsn.isasynccaller(tidx, target_tidx) = true;
259 lsn.isasynccaller(tidx, target_eidx) = true;
260 lsn.isasynccaller(aidx, target_tidx) = true;
261 lsn.isasynccaller(aidx, target_eidx) = true;
262 lsn.taskgraph(tidx, target_tidx) = 1;
263 lsn.graph(aidx, target_eidx) = 1;
264 end
265 end
266
267 for ap=1:length(tasks{t}.precedences)
268 pretype = tasks{t}.precedences(ap).preType;
269 posttype = tasks{t}.precedences(ap).postType;
270 preacts = tasks{t}.precedences(ap).preActs;
271 postacts = tasks{t}.precedences(ap).postActs;
272
273 % Validate PRE_AND activities exist before processing
274 if pretype == ActivityPrecedenceType.PRE_AND
275 if isempty(preacts)
276 line_error(mfilename, sprintf('PRE_AND precedence in task "%s" has no pre activities.', tasks{t}.name));
277 end
278 for prea = 1:length(preacts)
279 preaidx = findstring(lsn.hashnames, ['A:',preacts{prea}]);
280 if preaidx <= 0
281 line_error(mfilename, sprintf('PRE_AND precedence references non-existent activity "%s" in task "%s".', preacts{prea}, tasks{t}.name));
282 end
283 if preaidx > 0 && lsn.parent(preaidx) ~= tidx
284 line_error(mfilename, sprintf('PRE_AND precedence in task "%s" references activity "%s" from a different task.', tasks{t}.name, preacts{prea}));
285 end
286 end
287 end
288
289 % Validate POST_AND activities exist before processing
290 if posttype == ActivityPrecedenceType.POST_AND
291 if isempty(postacts)
292 line_error(mfilename, sprintf('POST_AND precedence in task "%s" has no post activities.', tasks{t}.name));
293 end
294 for posta = 1:length(postacts)
295 postaidx = findstring(lsn.hashnames, ['A:',postacts{posta}]);
296 if postaidx <= 0
297 line_error(mfilename, sprintf('POST_AND precedence references non-existent activity "%s" in task "%s".', postacts{posta}, tasks{t}.name));
298 end
299 if postaidx > 0 && lsn.parent(postaidx) ~= tidx
300 line_error(mfilename, sprintf('POST_AND precedence in task "%s" references activity "%s" from a different task.', tasks{t}.name, postacts{posta}));
301 end
302 end
303 end
304
305 for prea = 1:length(preacts)
306 preaidx = findstring(lsn.hashnames, ['A:',tasks{t}.precedences(ap).preActs{prea}]);
307 switch pretype
308 case ActivityPrecedenceType.PRE_AND
309 quorum = tasks{t}.precedences(ap).preParams;
310 if isempty(quorum)
311 preParam = 1.0;
312 else
313 preParam = quorum / length(preacts);
314 end
315 otherwise
316 preParam = 1.0;
317 end
318 switch posttype
319 case ActivityPrecedenceType.POST_OR
320 for posta = 1:length(postacts)
321 postaidx = findstring(lsn.hashnames, ['A:',tasks{t}.precedences(ap).postActs{posta}]);
322 probs = tasks{t}.precedences(ap).postParams;
323 postParam = probs(posta);
324 lsn.graph(preaidx, postaidx) = preParam * postParam;
325 lsn.actpretype(preaidx) = sparse(tasks{t}.precedences(ap).preType);
326 lsn.actposttype(postaidx) = sparse(tasks{t}.precedences(ap).postType);
327 end
328 case ActivityPrecedenceType.POST_AND
329 for posta = 1:length(postacts)
330 postaidx = findstring(lsn.hashnames, ['A:',tasks{t}.precedences(ap).postActs{posta}]);
331 lsn.graph(preaidx, postaidx) = 1;
332 lsn.actpretype(preaidx) = sparse(tasks{t}.precedences(ap).preType);
333 lsn.actposttype(postaidx) = sparse(tasks{t}.precedences(ap).postType);
334 end
335 case ActivityPrecedenceType.POST_LOOP
336 counts = tasks{t}.precedences(ap).postParams;
337 % add the end activity
338 enda = length(postacts);
339 loopentryaidx = findstring(lsn.hashnames, ['A:',tasks{t}.precedences(ap).preActs{1}]);
340 loopstartaidx = findstring(lsn.hashnames, ['A:',tasks{t}.precedences(ap).postActs{1}]);
341 loopendaidx = findstring(lsn.hashnames, ['A:',tasks{t}.precedences(ap).postActs{enda}]);
342 % list of activities inside postActs is treated as a
343 % serial dependence
344 curaidx = loopentryaidx;
345 for posta = 1:(length(postacts)-1) % last one is 'end' of loop activity
346 postaidx = findstring(lsn.hashnames, ['A:',tasks{t}.precedences(ap).postActs{posta}]);
347 lsn.graph(curaidx, postaidx) = 1.0;
348 lsn.actposttype(postaidx) = sparse((tasks{t}.precedences(ap).postType));
349 curaidx = postaidx;
350 end
351 loop_back_edges(curaidx, loopstartaidx) = true;
352 lsn.graph(curaidx, loopstartaidx) = 1.0 - 1.0 / counts;
353 lsn.graph(curaidx, loopendaidx) = 1.0 / counts;
354 lsn.actposttype(loopendaidx) = sparse((tasks{t}.precedences(ap).postType));
355 otherwise
356 for posta = 1:length(postacts)
357 postaidx = findstring(lsn.hashnames, ['A:',tasks{t}.precedences(ap).postActs{posta}]);
358 postParam = 1.0;
359 lsn.graph(preaidx, postaidx) = preParam * postParam;
360 lsn.actpretype(preaidx) = sparse(tasks{t}.precedences(ap).preType);
361 lsn.actposttype(postaidx) = sparse(tasks{t}.precedences(ap).postType);
362 end
363 end
364 end
365 end
366end
367
368%% Process forwarding calls from entries
369for e = 1:length(self.entries)
370 entry = self.entries{e};
371 eidx = findstring(lsn.hashnames, ['E:', entry.name]);
372 if eidx <= 0
373 eidx = findstring(lsn.hashnames, ['I:', entry.name]);
374 end
375 if eidx <= 0
376 continue;
377 end
378 source_tidx = lsn.parent(eidx);
379
380 for fw = 1:length(entry.forwardingDests)
381 target_entry_name = entry.forwardingDests{fw};
382 target_eidx = findstring(lsn.hashnames, ['E:', target_entry_name]);
383 if target_eidx <= 0
384 target_eidx = findstring(lsn.hashnames, ['I:', target_entry_name]);
385 end
386 if target_eidx <= 0
387 line_error(mfilename, sprintf('Entry "%s" forwards to non-existent entry "%s".', entry.name, target_entry_name));
388 end
389 target_tidx = lsn.parent(target_eidx);
390
391 % Validate: cannot forward to same task
392 if source_tidx == target_tidx
393 line_error(mfilename, sprintf('Entry "%s" cannot forward to entry "%s" on the same task.', entry.name, target_entry_name));
394 end
395
396 cidx = cidx + 1;
397 lsn.calltype(cidx, 1) = CallType.FWD;
398 lsn.callpair(cidx, 1:2) = [eidx, target_eidx];
399 lsn.callnames{cidx, 1} = [lsn.names{eidx}, '~>', lsn.names{target_eidx}];
400 lsn.callhashnames{cidx, 1} = [lsn.hashnames{eidx}, '~>', lsn.hashnames{target_eidx}];
401
402 % Forwarding probability (stored as mean calls)
403 fwdProb = entry.forwardingProbs(fw);
404 lsn.callproc{cidx, 1} = Geometric(1.0 / fwdProb);
405
406 % Update task graph to reflect forwarding relationship
407 lsn.taskgraph(source_tidx, target_tidx) = 1;
408 lsn.graph(eidx, target_eidx) = 1;
409 end
410end
411
412% Check for entries without boundTo activities
413unbound = find(all(~lsn.graph(lsn.eshift+1 : lsn.eshift+lsn.nentries, ...
414 lsn.ashift+1 : lsn.ashift+lsn.nacts), 2)); %#ok<EFIND>
415if ~isempty(unbound)
416 line_error(mfilename, 'An entry does not have any boundTo activity.');
417end
418
419%lsn.replies = false(1,lsn.nacts);
420%lsn.replygraph = 0*lsn.graph;
421for t = 1:lsn.ntasks
422 tidx = lsn.tshift+t;
423 for aidx = lsn.actsof{tidx}
424 postaidxs = find(lsn.graph(aidx, :));
425 isreply = true;
426 % if no successor is an action of tidx
427 for postaidx = postaidxs
428 if any(lsn.actsof{tidx} == postaidx)
429 isreply = false;
430 end
431 end
432 if isreply
433 % this is a leaf node, search backward for the parent entry,
434 % which is assumed to be unique
435 %lsn.replies(aidx-lsn.nacts) = true;
436 parentidx = aidx;
437 while lsn.type(parentidx) ~= LayeredNetworkElement.ENTRY
438 ancestors = find(lsn.graph(:,parentidx));
439 parentidx = at(ancestors,1); % only choose first ancestor
440 end
441 if lsn.type(parentidx) == LayeredNetworkElement.ENTRY
442 lsn.replygraph(aidx-lsn.ashift, parentidx-lsn.eshift) = true;
443 end
444 end
445 end
446end
447lsn.ncalls = size(lsn.calltype,1);
448
449% correct multiplicity for infinite server stations
450for tidx = find(lsn.sched == SchedStrategy.INF)
451 if lsn.type(tidx) == LayeredNetworkElement.TASK
452 callers = find(lsn.taskgraph(:, tidx));
453 callers_inf = strcmp(lsn.mult(callers), SchedStrategy.INF);
454 if any(callers_inf)
455 % if a caller is also inf, then we would need to recursively
456 % determine the maximum multiplicity, we instead use a
457 % heuristic value
458 lsn.mult(tidx) = sum(lsn.mult(~callers_inf)) + sum(callers_inf)*max(lsn.mult);
459 else
460 lsn.mult(tidx) = sum(lsn.mult(callers));
461 end
462 end
463end
464
465lsn.refset = zeros(lsn.nidx,1);
466[conncomps, roots]=graph_connected_components(lsn.taskgraph(lsn.nhosts+1:end, lsn.nhosts+1:end));
467lsn.conntasks = conncomps;
468for r=1:length(roots)
469 lsn.conntasks(find(lsn.conntasks == r)) = lsn.tshift+roots(r);
470end
471
472lsn.isref = lsn.sched == SchedStrategy.REF;
473lsn.iscache(1:(lsn.tshift+lsn.ntasks)) = lsn.nitems(1:(lsn.tshift+lsn.ntasks))>0;
474lsn.isfunction(1:(lsn.tshift+lsn.ntasks)) = ~cellfun(@isempty, lsn.setuptime(1:(lsn.tshift+lsn.ntasks)));
475
476% the dag differs from the graph:
477% - dag swaps the direction of entry-task edges
478% - dag removes loop edges
479dag = lsn.graph;
480n = size(dag, 1);
481% Reverse edges from TASK to ENTRY if not a reference
482for i = 1:n
483 if lsn.type(i) == LayeredNetworkElement.TASK && ~lsn.isref(i)
484 for j = 1:n
485 if lsn.type(j) == LayeredNetworkElement.ENTRY && dag(i,j)
486 dag(i,j) = 0;
487 dag(j,i) = 1;
488 end
489 end
490 end
491end
492
493% Compute entry-to-activity reachability within the same task
494for eoff = 1:lsn.nentries
495 eidx = lsn.eshift + eoff; % global entry index
496 tidx = lsn.parent(eidx); % parent task index
497 visited = false(1,nidx); % global visit mask
498 stack = eidx;
499 visited(eidx) = true;
500 while ~isempty(stack)
501 v = stack(end);
502 stack(end) = [];
503 nbrs = find(lsn.graph(v,:));
504 nbrs = nbrs(~visited(nbrs));
505 visited(nbrs) = true;
506 stack = [stack nbrs]; %#ok<AGROW>
507 end
508 acts = find(visited & ...
509 lsn.type' == LayeredNetworkElement.ACTIVITY & ...
510 lsn.parent == tidx);
511 lsn.actsof{lsn.eshift+eoff} = acts;
512end
513
514%% check for errors
515dag(loop_back_edges(:)) = 0;
516lsn.dag = dag;
517% Compute bounds on multiplicies for host processors and non-ref tasks
518if is_dag(dag)
519 lsn.maxmult = lsn_max_multiplicity(lsn);
520 lsn.maxmult = lsn.maxmult(1:(lsn.tshift+lsn.ntasks));
521else
522 line_error(mfilename, 'A cycle exists in an activity graph.');
523end
524% Check phase-2
525[replier_aidx,~] = find(lsn.replygraph);
526for r=1:length(replier_aidx)
527 aidx=lsn.ashift+replier_aidx(r);
528 for nextaidx=find(lsn.graph(aidx,:))
529 % if successor is an activity on the same task
530 if nextaidx > lsn.eshift+lsn.nentries
531 line_error(mfilename, 'Unsupported replyTo in non-terminal activity.');
532 end
533 end
534end
535% Check calls
536if ~isempty(lsn.callpair)
537 target_eidxs = unique(lsn.callpair(:,2));
538 for eidx=target_eidxs(:)'
539 call_types_to_eidx = lsn.calltype(find(lsn.callpair(:,2) == eidx),1);
540 if ~all(call_types_to_eidx == call_types_to_eidx(1))
541 line_error(mfilename, 'An entry is called both synchronously and asynchronously.');
542 end
543 end
544end
545
546self.lsn = lsn;
547end