1classdef LayeredNetwork < Model & Ensemble
2 % LayeredNetwork Hierarchical software performance modeling framework
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
10 % @brief Layered queueing network
for hierarchical software performance models
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
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
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
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);
43 % Copyright (c) 2012-2026, Imperial College London
44 % All rights reserved.
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)
62 %
public methods, including constructor
64 function self = reset(self, isHard)
79 function self = LayeredNetwork(name, filename)
80 % LAYEREDNETWORK Create a layered queueing network model
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
88 if nargin<1 %~exist(
'name',
'var')
89 [~,name]=fileparts(lineTempName);
100 if nargin>=2 %exist(
'filename',
'var')
101 self = LayeredNetwork.parseXML(filename, false);
106 function sn = summary(self)
112 plot(self, showTaskGraph)
113 plotGraph(self, useNodes)
114 plotGraphSimple(self, useNodes)
115 plotTaskGraph(self, useNodes)
119 idx = getNodeIndex(self,node)
120 node = getNodeByName(self,name)
121 [names,hostnames,tasknames,entrynames,actnames] = getNodeNames(self)
123 writeXML(self,filename,useAbstractNames);
128 LQN = getStruct(self);
130 function E = getNumberOfLayers(self)
131 % E = GETNUMBEROFLAYERS()
133 E = getNumberOfModels(self);
136 function E = getNumberOfModels(self)
137 % E = GETNUMBEROFMODELS()
139 if isempty(self.ensemble)
140 self.ensemble = getEnsemble(self);
142 E = length(self.ensemble);
145 function layers = getLayers(self)
146 % LAYERS = GETLAYERS()
148 layers = getEnsemble(self);
151 % setUsedFeatures : records that a certain language feature has been used
152 function self = setUsedLangFeature(self,e,className)
153 % SELF = SETUSEDLANGFEATURE(SELF,E,CLASSNAME)
155 self.usedFeatures{e}.setTrue(className);
158 function self = initUsedFeatures(self)
159 % SELF = INITUSEDFEATURES()
161 for e=1:getNumberOfModels(self)
162 self.usedFeatures{e} = SolverFeatureSet;
166 function usedFeatures = getUsedLangFeatures(self)
167 % USEDFEATURES = GETUSEDLANGFEATURES()
169 E = getNumberOfLayers(self);
170 usedFeatures = cell(1,E);
172 usedFeatures{e} = self.ensemble{e}.getUsedLangFeatures;
174 self.usedFeatures = usedFeatures;
178 jlqnmodel = JLINE.from_line_layered_network(self);
182 function result = nodeIndex(self, varargin)
183 % NODEINDEX Kotlin-style alias for getNodeIndex
184 result = self.getNodeIndex(varargin{:});
187 function result = nodeByName(self, varargin)
188 % NODEBYNAME Kotlin-style alias for getNodeByName
189 result = self.getNodeByName(varargin{:});
192 function result = nodeNames(self, varargin)
193 % NODENAMES Kotlin-style alias for getNodeNames
194 result = self.getNodeNames(varargin{:});
197 function result =
struct(self, varargin)
198 % STRUCT Kotlin-style alias
for getStruct
199 result = self.getStruct(varargin{:});
202 function result = numberOfLayers(self, varargin)
203 % NUMBEROFLAYERS Kotlin-style alias for getNumberOfLayers
204 result = self.getNumberOfLayers(varargin{:});
207 function result = numberOfModels(self, varargin)
208 % NUMBEROFMODELS Kotlin-style alias for getNumberOfModels
209 result = self.getNumberOfModels(varargin{:});
212 function result = layers(self, varargin)
213 % LAYERS Kotlin-style alias for getLayers
214 result = self.getLayers(varargin{:});
217 function result = usedLangFeatures(self, varargin)
218 % USEDLANGFEATURES Kotlin-style alias for getUsedLangFeatures
219 result = self.getUsedLangFeatures(varargin{:});
222 function sanitize(self)
224 % Validates
the LayeredNetwork configuration.
225 % Ensures that
if entries are defined, activities are also defined to serve those entries.
227 numEntries = length(self.entries);
228 numActivities = length(self.activities);
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);
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);
250 E = length(self.ensemble);
251 Msz = zeros(1,E); Ksz = zeros(1,E);
253 Msz(e) = self.ensemble{e}.getNumberOfStations;
254 Ksz(e) = self.ensemble{e}.getNumberOfClasses;
256 Roff = [0, cumsum(Msz(1:end-1))];
257 Coff = [0, cumsum(Ksz(1:end-1))];
260 function M = getNumberOfStations(self)
261 % M = GETNUMBEROFSTATIONS()
262 % Aggregate (sum over layers) station
count.
263 [~,~,Msz] = self.layerBlocks;
267 function K = getNumberOfClasses(self)
268 % K = GETNUMBEROFCLASSES()
269 % Aggregate (sum over layers)
class count.
270 [~,~,~,Ksz] = self.layerBlocks;
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);
281 for e=1:length(self.ensemble)
282 N = N + self.ensemble{e}.getNumberOfNodes;
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);
294 for e=1:length(self.ensemble)
295 N = N + self.ensemble{e}.getNumberOfStatefulNodes;
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;
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};
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
327 options = Solver.defaultOptions;
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);
336 % Getter methods
for API consistency with Java/Python
337 function val = getHosts(obj)
338 % GETHOSTS Get
the hosts/processors in
the network
342 function val = getTasks(obj)
343 % GETTASKS Get
the tasks in
the network
347 function val = getEntries(obj)
348 % GETENTRIES Get
the entries in
the network
352 function val = getActivities(obj)
353 % GETACTIVITIES Get
the activities in
the network
354 val = obj.activities;
360 function myLN = readXML(filename, verbose)
364 myLN = LayeredNetwork.parseXML(filename, verbose);
367 function myLN = load(filename, verbose)
371 myLN = LayeredNetwork.parseXML(filename, verbose);
374 myLN = parseXML(filename, verbose)
376 function myLN = fromNetwork(model)
377 myLN = QN2LQN(model);