LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
LQN2QN.m
1function model = LQN2QN(lqn)
2% LQN2QN Convert a LayeredNetwork (LQN) to a Network (QN)
3%
4% model = LQN2QN(lqn) converts a LayeredNetwork model to an equivalent
5% ordinary queueing network that approximates LQN blocking semantics.
6%
7% The conversion models synchronous call blocking by aggregating service
8% times: when a task makes a synchCall, its effective service time includes
9% both its own processing and all downstream processing (since it blocks
10% until the reply is received).
11%
12% This approach:
13% - Correctly models THROUGHPUT (validated against LQNS)
14% - Queue lengths are APPROXIMATED (blocking aggregated into entry task)
15%
16% For accurate queue length distribution across tiers, explicit blocking
17% with Reply signals would be required (future enhancement).
18%
19% Example:
20% lqn = LayeredNetwork('MyLQN');
21% % ... define LQN model ...
22% model = LQN2QN(lqn);
23% SolverDES(model).getAvgTable()
24%
25% Copyright (c) 2012-2026, Imperial College London
26% All rights reserved.
27
28%% Get LQN structure
29lsn = lqn.getStruct();
30
31%% Create QN model
32model = Network([lqn.getName(), '-QN']);
33
34%% Identify reference tasks
35refTaskIndices = find(lsn.isref);
36nRefTasks = length(refTaskIndices);
37
38if nRefTasks == 0
39 line_error(mfilename, 'LQN must have at least one reference task.');
40end
41
42%% Build call graph and compute effective service times
43effectiveService = zeros(1, lsn.ntasks + lsn.tshift);
44
45% First pass: get own service times
46for t = 1:lsn.ntasks
47 tidx = lsn.tshift + t;
48 effectiveService(tidx) = getTaskServiceDemand(lsn, tidx);
49end
50
51% Build downstream map
52downstreamTasks = cell(1, lsn.ntasks + lsn.tshift);
53for t = 1:lsn.ntasks
54 tidx = lsn.tshift + t;
55 downstreamTasks{tidx} = [];
56
57 entries = lsn.entriesof{tidx};
58 for eidx = entries
59 acts = lsn.actsof{eidx};
60 for aidx = acts
61 if lsn.type(aidx) ~= LayeredNetworkElement.ACTIVITY
62 continue;
63 end
64 if aidx > length(lsn.callsof) || isempty(lsn.callsof{aidx})
65 continue;
66 end
67 calls = lsn.callsof{aidx};
68 for cidx = calls
69 if full(lsn.calltype(cidx)) == CallType.SYNC
70 targetEidx = lsn.callpair(cidx, 2);
71 targetTidx = lsn.parent(targetEidx);
72 downstreamTasks{tidx} = [downstreamTasks{tidx}, targetTidx];
73 end
74 end
75 end
76 end
77end
78
79% Compute effective service times (includes all downstream blocking time)
80computed = false(1, lsn.ntasks + lsn.tshift);
81for iter = 1:lsn.ntasks
82 changed = false;
83 for t = 1:lsn.ntasks
84 tidx = lsn.tshift + t;
85 if computed(tidx)
86 continue;
87 end
88
89 downstream = downstreamTasks{tidx};
90 if isempty(downstream)
91 computed(tidx) = true;
92 changed = true;
93 else
94 allDownstreamComputed = true;
95 for dt = downstream
96 if ~computed(dt)
97 allDownstreamComputed = false;
98 break;
99 end
100 end
101
102 if allDownstreamComputed
103 for dt = downstream
104 effectiveService(tidx) = effectiveService(tidx) + effectiveService(dt);
105 end
106 computed(tidx) = true;
107 changed = true;
108 end
109 end
110 end
111 if ~changed
112 break;
113 end
114end
115
116%% Create nodes
117procNodes = cell(lsn.nhosts, 1);
118thinkNodes = cell(lsn.ntasks, 1);
119
120% Find entry tasks for reference tasks
121entryTaskForRef = zeros(1, nRefTasks);
122for rt = 1:nRefTasks
123 tidx = refTaskIndices(rt);
124 downstream = downstreamTasks{tidx};
125 if ~isempty(downstream)
126 entryTaskForRef(rt) = downstream(1);
127 end
128end
129
130% Create processor nodes for entry tasks
131for rt = 1:nRefTasks
132 entryTidx = entryTaskForRef(rt);
133 if entryTidx <= 0
134 continue;
135 end
136
137 procIdx = lsn.parent(entryTidx);
138 if isempty(procIdx) || procIdx <= 0 || procIdx > lsn.nhosts || ~isempty(procNodes{procIdx})
139 continue;
140 end
141
142 procName = lsn.names{procIdx};
143 nServers = lsn.mult(procIdx);
144 sched = lsn.sched(procIdx);
145
146 if isinf(nServers) || sched == SchedStrategy.INF
147 procNodes{procIdx} = Delay(model, procName);
148 else
149 procNodes{procIdx} = Queue(model, procName, sched);
150 procNodes{procIdx}.setNumberOfServers(nServers);
151 end
152end
153
154% Create think nodes
155for rt = 1:nRefTasks
156 tidx = refTaskIndices(rt);
157 taskName = lsn.names{tidx};
158 t = tidx - lsn.tshift;
159 thinkNodes{t} = Delay(model, [taskName, '_Think']);
160end
161
162%% Create job classes and set service times
163jobClasses = cell(nRefTasks, 1);
164
165for rt = 1:nRefTasks
166 tidx = refTaskIndices(rt);
167 taskName = lsn.names{tidx};
168 t = tidx - lsn.tshift;
169 thinkNode = thinkNodes{t};
170 N = lsn.mult(tidx);
171
172 jobClass = ClosedClass(model, [taskName, '_Job'], N, thinkNode);
173 jobClasses{rt} = jobClass;
174
175 % Think time
176 thinkDist = lsn.think{tidx};
177 if isa(thinkDist, 'Immediate') || thinkDist.getMean() < GlobalConstants.FineTol
178 thinkDist = Exp(1e8);
179 end
180 thinkNode.setService(jobClass, thinkDist);
181
182 % Effective service at entry task's processor
183 entryTidx = entryTaskForRef(rt);
184 if entryTidx > 0
185 entryProcIdx = lsn.parent(entryTidx);
186 if ~isempty(entryProcIdx) && entryProcIdx > 0 && entryProcIdx <= lsn.nhosts && ~isempty(procNodes{entryProcIdx})
187 procNode = procNodes{entryProcIdx};
188 effService = effectiveService(entryTidx);
189
190 if effService > GlobalConstants.FineTol
191 procNode.setService(jobClass, Exp(1/effService));
192 else
193 procNode.setService(jobClass, Immediate());
194 end
195 end
196 end
197end
198
199%% Build routing matrix
200P = model.initRoutingMatrix();
201
202for rt = 1:nRefTasks
203 tidx = refTaskIndices(rt);
204 t = tidx - lsn.tshift;
205 thinkNode = thinkNodes{t};
206 jobClass = jobClasses{rt};
207
208 entryTidx = entryTaskForRef(rt);
209 if entryTidx <= 0
210 P{jobClass, jobClass}(thinkNode, thinkNode) = 1.0;
211 continue;
212 end
213
214 entryProcIdx = lsn.parent(entryTidx);
215 if isempty(entryProcIdx) || entryProcIdx <= 0 || entryProcIdx > lsn.nhosts || isempty(procNodes{entryProcIdx})
216 P{jobClass, jobClass}(thinkNode, thinkNode) = 1.0;
217 continue;
218 end
219
220 procNode = procNodes{entryProcIdx};
221 P{jobClass, jobClass}(thinkNode, procNode) = 1.0;
222 P{jobClass, jobClass}(procNode, thinkNode) = 1.0;
223end
224
225model.link(P);
226
227end
228
229%% Helper: Get service demand for a task
230function demand = getTaskServiceDemand(lsn, tidx)
231 demand = 0;
232
233 entries = lsn.entriesof{tidx};
234 for eidx = entries
235 acts = lsn.actsof{eidx};
236 for aidx = acts
237 if lsn.type(aidx) == LayeredNetworkElement.ACTIVITY
238 hostDem = lsn.hostdem{aidx};
239 if isa(hostDem, 'Distribution')
240 demand = demand + hostDem.getMean();
241 end
242 end
243 end
244 end
245end
Definition mmt.m:92