LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
LayeredNetwork.m
1classdef LayeredNetwork < Model & Ensemble
2 % LayeredNetwork Hierarchical software performance modeling framework
3 %
4 % LayeredNetwork implements Layered Queueing Networks (LQN) for modeling
5 % hierarchical software systems with clients, application servers, and
6 % resource layers. It supports modeling of complex software architectures
7 % with tasks, entries, activities, and their interactions across multiple
8 % system layers.
9 %
10 % @brief Layered queueing network for hierarchical software performance models
11 %
12 % Key characteristics:
13 % - Hierarchical multi-layer architecture modeling
14 % - Software-centric performance analysis
15 % - Task and entry abstraction levels
16 % - Activity-based detailed modeling
17 % - Host resource modeling
18 % - Client-server interaction patterns
19 %
20 % LQN model components:
21 % - Hosts: Physical or logical processing resources
22 % - Tasks: Software processes or services
23 % - Entries: Service request entry points
24 % - Activities: Detailed task execution steps
25 % - Reference tasks: External workload generators
26 %
27 % LayeredNetwork is used for:
28 % - Software performance engineering
29 % - Multi-tier application modeling
30 % - Microservice architecture analysis
31 % - Distributed system performance evaluation
32 % - Capacity planning for software systems
33 %
34 % Example:
35 % @code
36 % lqn = LayeredNetwork('WebApp');
37 % client = Host(lqn, 'ClientTier', Inf);
38 % server = Host(lqn, 'ServerTier', 4);
39 % web_task = Task(lqn, 'WebServer', 1, server);
40 % request_entry = Entry(lqn, 'ProcessRequest', web_task);
41 % @endcode
42 %
43 % Copyright (c) 2012-2026, Imperial College London
44 % All rights reserved.
45
46 properties (Hidden)
47 lsn;
48 usedFeatures; % cell with structures of booleans listing the used classes
49 % it must be accessed via getUsedLangFeatures
50 obj = []; % jline.lang.layered.LayeredNetwork mirror (set by Java-backed solvers)
51 end
52
53 properties
54 hosts = [];
55 tasks = [];
56 reftasks = [];
57 activities = [];
58 entries = [];
59 end
60
61 methods
62 %public methods, including constructor
63
64 function self = reset(self, isHard)
65 if nargin<2
66 isHard = false;
67 end
68 self.ensemble = {};
69 if isHard
70 self.hosts = {};
71 self.tasks = {};
72 self.reftasks = {};
73 self.entries = {};
74 self.activities = {};
75 end
76 end
77
78 % constructor
79 function self = LayeredNetwork(name, filename)
80 % LAYEREDNETWORK Create a layered queueing network model
81 %
82 % @brief Creates a LayeredNetwork instance for hierarchical modeling
83 % @param name String identifier for the layered model
84 % @param filename Optional filename for model import/export
85 % @return self LayeredNetwork instance ready for hierarchical modeling
86
87 self@Ensemble({})
88 if nargin<1 %~exist('name','var')
89 [~,name]=fileparts(lineTempName);
90 end
91 name = char(name);
92 self@Model(name);
93 self.ensemble = {};
94 self.hosts = {};
95 self.tasks = {};
96 self.reftasks = {};
97 self.entries = {};
98 self.activities = {};
99
100 if nargin>=2 %exist('filename','var')
101 self = LayeredNetwork.parseXML(filename, false);
102 end
103 end
104
105
106 function sn = summary(self)
107 % sn = SUMMARY()
108
109 sn = self.getStruct;
110 end
111
112 plot(self, showTaskGraph)
113 plotGraph(self, useNodes)
114 plotGraphSimple(self, useNodes)
115 plotTaskGraph(self, useNodes)
116 end
117
118 methods
119 idx = getNodeIndex(self,node)
120 node = getNodeByName(self,name)
121 [names,hostnames,tasknames,entrynames,actnames] = getNodeNames(self)
122
123 writeXML(self,filename,useAbstractNames);
124 end
125
126 methods
127
128 LQN = getStruct(self);
129
130 function E = getNumberOfLayers(self)
131 % E = GETNUMBEROFLAYERS()
132
133 E = getNumberOfModels(self);
134 end
135
136 function E = getNumberOfModels(self)
137 % E = GETNUMBEROFMODELS()
138
139 if isempty(self.ensemble)
140 self.ensemble = getEnsemble(self);
141 end
142 E = length(self.ensemble);
143 end
144
145 function layers = getLayers(self)
146 % LAYERS = GETLAYERS()
147
148 layers = getEnsemble(self);
149 end
150
151 % setUsedFeatures : records that a certain language feature has been used
152 function self = setUsedLangFeature(self,e,className)
153 % SELF = SETUSEDLANGFEATURE(SELF,E,CLASSNAME)
154
155 self.usedFeatures{e}.setTrue(className);
156 end
157
158 function self = initUsedFeatures(self)
159 % SELF = INITUSEDFEATURES()
160
161 for e=1:getNumberOfModels(self)
162 self.usedFeatures{e} = SolverFeatureSet;
163 end
164 end
165
166 function usedFeatures = getUsedLangFeatures(self)
167 % USEDFEATURES = GETUSEDLANGFEATURES()
168
169 E = getNumberOfLayers(self);
170 usedFeatures = cell(1,E);
171 for e=1:E
172 usedFeatures{e} = self.ensemble{e}.getUsedLangFeatures;
173 end
174 self.usedFeatures = usedFeatures;
175 end
176
177 function view(self)
178 jlqnmodel = JLINE.from_line_layered_network(self);
179 jlqnmodel.view();
180 end
181
182 function result = nodeIndex(self, varargin)
183 % NODEINDEX Kotlin-style alias for getNodeIndex
184 result = self.getNodeIndex(varargin{:});
185 end
186
187 function result = nodeByName(self, varargin)
188 % NODEBYNAME Kotlin-style alias for getNodeByName
189 result = self.getNodeByName(varargin{:});
190 end
191
192 function result = nodeNames(self, varargin)
193 % NODENAMES Kotlin-style alias for getNodeNames
194 result = self.getNodeNames(varargin{:});
195 end
196
197 function result = struct(self, varargin)
198 % STRUCT Kotlin-style alias for getStruct
199 result = self.getStruct(varargin{:});
200 end
201
202 function result = numberOfLayers(self, varargin)
203 % NUMBEROFLAYERS Kotlin-style alias for getNumberOfLayers
204 result = self.getNumberOfLayers(varargin{:});
205 end
206
207 function result = numberOfModels(self, varargin)
208 % NUMBEROFMODELS Kotlin-style alias for getNumberOfModels
209 result = self.getNumberOfModels(varargin{:});
210 end
211
212 function result = layers(self, varargin)
213 % LAYERS Kotlin-style alias for getLayers
214 result = self.getLayers(varargin{:});
215 end
216
217 function result = usedLangFeatures(self, varargin)
218 % USEDLANGFEATURES Kotlin-style alias for getUsedLangFeatures
219 result = self.getUsedLangFeatures(varargin{:});
220 end
221
222 function sanitize(self)
223 % SANITIZE()
224 % Validates the LayeredNetwork configuration.
225 % Ensures that if entries are defined, activities are also defined to serve those entries.
226
227 numEntries = length(self.entries);
228 numActivities = length(self.activities);
229
230 if numEntries > 0 && numActivities == 0
231 msg = sprintf('LayeredNetwork ''%s'' has %d entry(ies) but no activities. Entries must be bound to activities to form a valid LQN model. Use activity.boundTo(entry) to establish the binding.', self.name, numEntries);
232 line_error(mfilename, msg);
233 end
234 end
235
236 % Aggregate flat interface (Network-compatible) used by SolverENV.
237 % An LQN is exposed as the block-diagonal union of its layer
238 % networks: aggregate station/class/node counts are the sums over
239 % layers, and matrices are laid out block-diagonally in the exact
240 % ordering produced by SolverLN.getTranAvg. layerBlocks returns the
241 % per-layer cumulative offsets and sizes that all these methods and
242 % the SolverENV labeling share as a single source of truth.
243 function [Roff, Coff, Msz, Ksz] = layerBlocks(self)
244 % [ROFF,COFF,MSZ,KSZ] = LAYERBLOCKS()
245 % Roff(e)/Coff(e): row/col offset (0-based) of layer e's block;
246 % Msz(e)/Ksz(e): number of stations/classes of layer e.
247 if isempty(self.ensemble)
248 self.ensemble = getEnsemble(self);
249 end
250 E = length(self.ensemble);
251 Msz = zeros(1,E); Ksz = zeros(1,E);
252 for e=1:E
253 Msz(e) = self.ensemble{e}.getNumberOfStations;
254 Ksz(e) = self.ensemble{e}.getNumberOfClasses;
255 end
256 Roff = [0, cumsum(Msz(1:end-1))];
257 Coff = [0, cumsum(Ksz(1:end-1))];
258 end
259
260 function M = getNumberOfStations(self)
261 % M = GETNUMBEROFSTATIONS()
262 % Aggregate (sum over layers) station count.
263 [~,~,Msz] = self.layerBlocks;
264 M = sum(Msz);
265 end
266
267 function K = getNumberOfClasses(self)
268 % K = GETNUMBEROFCLASSES()
269 % Aggregate (sum over layers) class count.
270 [~,~,~,Ksz] = self.layerBlocks;
271 K = sum(Ksz);
272 end
273
274 function N = getNumberOfNodes(self)
275 % N = GETNUMBEROFNODES()
276 % Aggregate (sum over layers) node count.
277 if isempty(self.ensemble)
278 self.ensemble = getEnsemble(self);
279 end
280 N = 0;
281 for e=1:length(self.ensemble)
282 N = N + self.ensemble{e}.getNumberOfNodes;
283 end
284 end
285
286 function N = getNumberOfStatefulNodes(self)
287 % N = GETNUMBEROFSTATEFULNODES()
288 % Aggregate (sum over layers) stateful-node count. Used by
289 % Environment.addStage to validate stage compatibility.
290 if isempty(self.ensemble)
291 self.ensemble = getEnsemble(self);
292 end
293 N = 0;
294 for e=1:length(self.ensemble)
295 N = N + self.ensemble{e}.getNumberOfStatefulNodes;
296 end
297 end
298
299 function [Qt, Ut, Tt] = getTranHandles(self)
300 % [QT,UT,TT] = GETTRANHANDLES()
301 % Block-diagonal aggregate transient handles over the layer
302 % networks. Off-diagonal cells are left empty (no cross-layer
303 % station/class pairing); SolverLN.getTranAvg reproduces the same
304 % block-diagonal layout so the handles only fix the M x K shape.
305 [Roff, Coff, Msz, Ksz] = self.layerBlocks;
306 M = sum(Msz); K = sum(Ksz);
307 Qt = cell(M,K); Ut = cell(M,K); Tt = cell(M,K);
308 for e=1:length(self.ensemble)
309 [Qe, Ue, Te] = self.ensemble{e}.getTranHandles;
310 for i=1:Msz(e)
311 for r=1:Ksz(e)
312 Qt{Roff(e)+i, Coff(e)+r} = Qe{i,r};
313 Ut{Roff(e)+i, Coff(e)+r} = Ue{i,r};
314 Tt{Roff(e)+i, Coff(e)+r} = Te{i,r};
315 end
316 end
317 end
318 end
319
320 function initFromMarginal(self, n, options)
321 % INITFROMMARGINAL(N, OPTIONS)
322 % Split the aggregate (M x K) marginal queue-length matrix N into
323 % per-layer blocks and warm-start each layer network. This is the
324 % state-continuity mechanism used by SolverENV across environment
325 % switches.
326 if nargin<3
327 options = Solver.defaultOptions;
328 end
329 [Roff, Coff, Msz, Ksz] = self.layerBlocks;
330 for e=1:length(self.ensemble)
331 block = n(Roff(e)+1:Roff(e)+Msz(e), Coff(e)+1:Coff(e)+Ksz(e));
332 self.ensemble{e}.initFromMarginal(block, options);
333 end
334 end
335
336 % Getter methods for API consistency with Java/Python
337 function val = getHosts(obj)
338 % GETHOSTS Get the hosts/processors in the network
339 val = obj.hosts;
340 end
341
342 function val = getTasks(obj)
343 % GETTASKS Get the tasks in the network
344 val = obj.tasks;
345 end
346
347 function val = getEntries(obj)
348 % GETENTRIES Get the entries in the network
349 val = obj.entries;
350 end
351
352 function val = getActivities(obj)
353 % GETACTIVITIES Get the activities in the network
354 val = obj.activities;
355 end
356
357 end
358
359 methods (Static)
360 function myLN = readXML(filename, verbose)
361 if nargin < 2
362 verbose = false;
363 end
364 myLN = LayeredNetwork.parseXML(filename, verbose);
365 end
366
367 function myLN = load(filename, verbose)
368 if nargin < 2
369 verbose = false;
370 end
371 myLN = LayeredNetwork.parseXML(filename, verbose);
372 end
373
374 myLN = parseXML(filename, verbose)
375
376 function myLN = fromNetwork(model)
377 myLN = QN2LQN(model);
378 end
379 end
380end
Definition Station.m:245