LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
SolverLDES.m
1classdef SolverLDES < NetworkSolver
2 % SolverLDES LINE Discrete Event Simulator solver using SSJ library
3 %
4 % SolverLDES implements a discrete-event simulation solver that uses the SSJ
5 % (Stochastic Simulation in Java) library to analyze queueing networks.
6 % It supports open and closed networks with various service distributions,
7 % scheduling strategies, and advanced node types.
8 %
9 % For LayeredNetwork (LQN) models, SolverLDES also supports LDES simulation.
10 %
11 % @brief Discrete-event simulation solver using SSJ library
12 %
13 % Example:
14 % @code
15 % solver = SolverLDES(model, 'samples', 1000000, 'seed', 23000);
16 % solver.getAvg(); % Run LDES simulation
17 % @endcode
18 %
19 % Passing an auxiliary solver as first optional argument warm-starts the
20 % simulation from that solver's steady-state solution (see initFromSolver):
21 % @code
22 % solver = SolverLDES(model, SolverMVA(model), 'samples', 50000);
23 % @endcode
24 %
25 % Copyright (c) 2012-2026, Imperial College London
26 % All rights reserved.
27
28 methods
29 function self = SolverLDES(model, varargin)
30 % SOLVERLDES Create an LDES solver instance
31 %
32 % @brief Creates a Discrete Event Simulation solver
33 % @param model Network model or path to a JMT file (.jsimg/.jsim/.jsimw/.jmva)
34 % @param varargin Optional parameters (samples, seed, method, etc.)
35 % @return self SolverLDES instance configured for simulation
36
37 % Accept a JMT file path in place of a Network object
38 if ischar(model) || isstring(model)
39 model = JMT2LINE(char(model));
40 end
41 % An auxiliary solver passed as first optional argument requests a
42 % warm start: its steady-state distribution decides the initial
43 % simulation state (see initFromSolver).
44 initSolver = [];
45 if ~isempty(varargin) && isa(varargin{1}, 'NetworkSolver')
46 initSolver = varargin{1};
47 varargin(1) = [];
48 end
49 % LayeredNetwork (LQN) models are solved by the Java LDES backend
50 % directly; attach the jline mirror before the superclass
51 % constructor so the Network-specific initialization is skipped.
52 if isa(model, 'LayeredNetwork') && isempty(model.obj)
53 model.obj = JLINE.from_line_layered_network(model);
54 end
55 self@NetworkSolver(model, mfilename);
56 self.setOptions(Solver.parseOptions(varargin, self.defaultOptions));
57 self.options.lang = 'java';
58 if isa(self.model, 'LayeredNetwork')
59 % LayeredNetwork (LQN) models are solved by the Java LDES
60 % ensemble backend; setLang builds self.obj (JLINE.SolverLDES).
61 self.setLang();
62 end
63 % Regular Network models are fully JSON-mediated: the model is NOT
64 % marshalled to a Java object (no self.obj, no model.obj), so
65 % runAnalyzer uses the subprocess JSON path and repeated solvers keep
66 % independent MATLAB-side transient handles (NetworkSolver.initHandles).
67 if ~isempty(initSolver)
68 self.initFromSolver(initSolver);
69 end
70 end
71
72 function sn = getStruct(self)
73 % QN = GETSTRUCT()
74
75 % Get data structure summarizing the model
76 if isa(self.model, 'LayeredNetwork')
77 sn = self.model.getStruct();
78 else
79 sn = self.model.getStruct(true);
80 end
81 end
82
83 function [allMethods] = listValidMethods(self)
84 % allMethods = LISTVALIDMETHODS()
85 % List valid methods for this solver
86
87 allMethods = {'default'};
88 end
89
90 function bool = isStochasticMethod(self, method) %#ok<INUSD>
91 % BOOL = ISSTOCHASTICMETHOD(METHOD)
92 % LDES is a discrete-event simulator; all methods are stochastic.
93 bool = true;
94 end
95 end
96
97 methods (Static)
98
99 function featSupported = getFeatureSet()
100 % FEATSUPPORTED = GETFEATURESET()
101
102 featSupported = SolverFeatureSet;
103 featSupported.setTrue({'Sink', 'Source', ...
104 'Queue', 'Delay', ...
105 'Fork', 'Join', 'Forker', 'Joiner', ... % Fork-Join node support
106 'Place', 'Transition', ... % Petri net node support
107 'QueueingPlace', ... % Queueing place (QPN embedded queue): FCFS/LCFS/SIRO/INF, renewal service
108 'Linkage', 'Enabling', 'Inhibiting', 'Timing', 'Firing', 'Storage', ... % Petri net section support
109 'Logger', 'LogTunnel', ... % Logger node support
110 'Buffer', ... % Finite buffer capacity support
111 'Region', ... % Finite capacity region support
112 'Exp', 'Erlang', 'HyperExp', 'PH', 'APH', 'Coxian', 'Cox2', 'MAP', 'DMAP', 'MMAP', 'BMAP', 'MMPP2', 'ME', 'RAP', 'Immediate', 'Disabled', 'Replayer', 'Trace', ... % Trace is an alias of Replayer
113 'Det', 'Uniform', 'Gamma', 'Pareto', 'Weibull', 'Lognormal', ... % Additional continuous distributions
114 'Geometric', ... % Lattice-valued interarrival/service time on {1,2,...} (Geo/Geo/1 and slotted models)
115 'Bernoulli', 'Binomial', 'Poisson', ... % Counting distributions; zero atom becomes an immediate interval (continuous mode only)
116 'NHPP', ... % Piecewise-constant-intensity non-homogeneous Poisson process
117 'Server', 'JobSink', 'RandomSource', ...
118 'InfiniteServer', 'SharedServer', 'ServiceTunnel', 'DelayStation', ... % internal station-section markers
119 'SchedStrategy_FCFS', 'SchedStrategy_INF', ...
120 'SchedStrategy_HOL', ... % Priority scheduling (FCFS with priorities)
121 'SchedStrategy_FCFSPRIO', ... % FCFS with priorities (non-preemptive)
122 'SchedStrategy_PS', ... % Processor Sharing
123 'SchedStrategy_DPS', ... % Discriminatory Processor Sharing
124 'SchedStrategy_GPS', ... % Generalized Processor Sharing
125 'SchedStrategy_LCFS', ... % Last Come First Served (non-preemptive)
126 'SchedStrategy_LCFSPR', ... % LCFS Preemptive Resume
127 'SchedStrategy_LCFSPI', ... % LCFS Preemptive Independent
128 'SchedStrategy_FCFSPR', ... % FCFS Preemptive Resume
129 'SchedStrategy_FCFSPI', ... % FCFS Preemptive Independent
130 'SchedStrategy_LPS', ... % Longest Processing time first Shortest
131 'SchedStrategy_SIRO', ...
132 'SchedStrategy_SJF', 'SchedStrategy_LJF', ...
133 'SchedStrategy_LEPT', ...
134 'SchedStrategy_SEPT', ...
135 'SchedStrategy_SRPT', ... % Shortest Remaining Processing Time (preemptive)
136 'SchedStrategy_SRPTPRIO', ... % SRPT with priorities
137 'SchedStrategy_PSJF', ... % Preemptive Shortest Job First
138 'SchedStrategy_FB', ... % Feedback / Least Attained Service
139 'SchedStrategy_LRPT', ... % Longest Remaining Processing Time
140 'SchedStrategy_EXT', ...
141 'SchedStrategy_POLLING', ... % Polling scheduling (GATED, EXHAUSTIVE, KLIMITED)
142 'SchedStrategy_PSPRIO', 'SchedStrategy_DPSPRIO', 'SchedStrategy_GPSPRIO', ... % PS/DPS/GPS with priorities
143 'SchedStrategy_LCFSPRIO', 'SchedStrategy_LCFSPRPRIO', 'SchedStrategy_LCFSPIPRIO', ... % LCFS priority variants
144 'SchedStrategy_FCFSPRPRIO', 'SchedStrategy_FCFSPIPRIO', ... % FCFS preemptive priority variants
145 'SchedStrategy_FSP', ... % Fair Sojourn Protocol (virtual PS finish time ranking)
146 'SchedStrategy_PAS', ... % Pass-and-swap (order-independent) queue
147 'SchedStrategy_OI', ... % Order-independent queue (PAS with empty swap graph)
148 'SchedStrategy_EDD', 'SchedStrategy_EDF', 'SchedStrategy_SETF', ... % Deadline/elapsed-time disciplines
149 'Router', 'Dispatcher', ... % Router node support (Dispatcher is the internal router section)
150 'ClassSwitch', 'StatelessClassSwitcher', ... % Class switching node support
151 'Cache', 'CacheClassSwitcher', ... % Cache node support with replacement policies (LRU, FIFO, Strict FIFO, RR)
152 'CacheRetrieval', ...
153 'RoutingStrategy_PROB', 'RoutingStrategy_RAND', ...
154 'RoutingStrategy_RROBIN', 'RoutingStrategy_WRROBIN', ...
155 'RoutingStrategy_JSQ', ... % Join the Shortest Queue
156 'RoutingStrategy_KCHOICES', ... % Power of K Choices routing
157 'OpenClass', ...
158 'ClosedClass', ...
159 'SelfLoopingClass', ...
160 'OpenSignal', ... % G-network signal class in open networks
161 'ClosedSignal', ... % G-network signal class in closed networks
162 'SignalType_NEGATIVE', ...
163 'SignalType_REPLY', ...
164 'SignalType_CATASTROPHE', ...
165 'SignalBatchRemoval', ... % Engine reads sn.signalremdist
166 'SignalRemovalPolicy', ... % Engine reads sn.signalrempolicy
167 'LoadDependence', ... % Load-dependent service rates
168 'ClassDependence', ... % Class-dependent service rate handles (setLimitedClassDependence)
169 'SetupDelayOff', ... % Engine simulates the SETUP/DELAYOFF server states
170 'Balking', ... % Engine reads sn.balkingStrategy / balkingThresholds
171 'Reneging', ... % Engine collects renegingRate / avgRenegingWaitTime
172 'Retrial', ... % Engine collects retrialDropped and successful retries
173 'ReplacementStrategy_RR', 'ReplacementStrategy_FIFO', 'ReplacementStrategy_SFIFO', 'ReplacementStrategy_LRU',...
174 'ReplacementStrategy_HLRU','ReplacementStrategy_CLIMB','ReplacementStrategy_QLRU'});
175 end
176
177 function [bool, featSupported] = supports(model)
178 % [BOOL, FEATSUPPORTED] = SUPPORTS(MODEL)
179
180 if isa(model, 'LayeredNetwork')
181 % LayeredNetwork models are simulated by the Java LDES
182 % backend (jline.solvers.ldes.SolverLDES), which validates
183 % the LQN feature set at run time.
184 bool = true;
185 featSupported = SolverFeatureSet;
186 return;
187 end
188
189 % Regular Network support
190 featUsed = model.getUsedLangFeatures();
191 featSupported = SolverLDES.getFeatureSet();
192 bool = SolverFeatureSet.supports(featSupported, featUsed);
193 end
194
195 function options = defaultOptions()
196 % OPTIONS = DEFAULTOPTIONS()
197
198 options = SolverOptions('LDES');
199 end
200
201 function commonDir = getLdesCommonDir()
202 % COMMONDIR = GETLDESCOMMONDIR()
203 % Directory holding common/jline.jar and the optional native
204 % GraalVM ldes binary. Resolved from the jline.jar entry on the
205 % Java classpath (robust across checkouts and installs), falling
206 % back to ascending from this class file to <root>/common.
207 commonDir = '';
208 try
209 cp = javaclasspath('-all');
210 catch
211 cp = {};
212 end
213 for i = 1:numel(cp)
214 [pdir, nm, ext] = fileparts(char(cp{i}));
215 if strcmpi([nm ext], 'jline.jar')
216 commonDir = pdir;
217 return;
218 end
219 end
220 % Fallback: <repo>/common relative to this file
221 % (.../matlab/src/solvers/wrappers/LDES/@SolverLDES -> <repo> is 6 levels up).
222 root = fileparts(mfilename('fullpath'));
223 for k = 1:6
224 root = fileparts(root);
225 end
226 cand = fullfile(root, 'common');
227 if exist(cand, 'dir')
228 commonDir = cand;
229 end
230 end
231
232 function m = elfMachine(path)
233 % M = ELFMACHINE(PATH)
234 % ELF e_machine identifier of a binary (header offset 0x12, 2
235 % bytes), or [] if PATH is not a readable ELF file.
236 m = [];
237 fid = fopen(path, 'r');
238 if fid < 0
239 return;
240 end
241 hdr = fread(fid, 20, '*uint8');
242 fclose(fid);
243 if numel(hdr) < 20
244 return;
245 end
246 % Magic: 0x7F 'E' 'L' 'F'
247 if ~(hdr(1) == 127 && hdr(2) == uint8('E') && hdr(3) == uint8('L') && hdr(4) == uint8('F'))
248 return;
249 end
250 % EI_DATA (index 6, 1-based): 1 = little-endian, 2 = big-endian.
251 % e_machine is a 2-byte field at offset 0x12 (indices 19,20).
252 if hdr(6) == 1
253 m = double(hdr(19)) + double(hdr(20)) * 256;
254 else
255 m = double(hdr(19)) * 256 + double(hdr(20));
256 end
257 end
258
259 function m = hostElfMachine()
260 % M = HOSTELFMACHINE()
261 % Expected ELF e_machine for the current MATLAB host CPU, or [] if
262 % unknown. MATLAB on Linux ships as glnxa64 (x86-64); glnxaa64
263 % (aarch64) is mapped for completeness.
264 switch computer('arch')
265 case 'glnxa64'
266 m = 62; % 0x3E EM_X86_64
267 case 'glnxaa64'
268 m = 183; % 0xB7 EM_AARCH64
269 otherwise
270 m = [];
271 end
272 end
273
274 function p = getLdesNativePath()
275 % P = GETLDESNATIVEPATH()
276 % Path to a runnable native LDES binary (common/ldes), or '' if
277 % none is usable. Mirrors the Python-native selection: the native
278 % binary is only used on Linux, and a binary whose ELF architecture
279 % does not match the host CPU (e.g. an x86-64 build on an aarch64
280 % host) is ignored so the caller can fall back to the in-process
281 % JLINE (JVM) backend. If host or binary architecture cannot be
282 % determined, the binary is used as a best effort.
283 p = '';
284 if ~(isunix && ~ismac) % Linux only
285 return;
286 end
287 commonDir = SolverLDES.getLdesCommonDir();
288 if isempty(commonDir)
289 return;
290 end
291 cand = fullfile(commonDir, 'ldes');
292 if exist(cand, 'file') ~= 2
293 return;
294 end
295 hostm = SolverLDES.hostElfMachine();
296 binm = SolverLDES.elfMachine(cand);
297 if ~isempty(hostm) && ~isempty(binm) && hostm ~= binm
298 return; % present but built for a different CPU architecture
299 end
300 p = cand;
301 end
302
303 function runners = getLdesRunners()
304 % RUNNERS = GETLDESRUNNERS()
305 % Ordered list of command prefixes that run the LDES engine on a
306 % "solve ..." argument list, exchanging only JSON. The native GraalVM
307 % binary (common/ldes) is tried first for fast startup; the full-JVM
308 % "<java> -jar common/ldes.jar" is the fallback (same shaded engine).
309 % The JVM fallback is needed because the AOT native binary lacks some
310 % reflective/serialization features (e.g. the fork-join MMT transform
311 % serializes jline.lang.Model). Returns a cellstr (possibly empty).
312 runners = {};
313 nativePath = SolverLDES.getLdesNativePath();
314 if ~isempty(nativePath)
315 runners{end+1} = sprintf('"%s"', nativePath);
316 end
317 commonDir = SolverLDES.getLdesCommonDir();
318 if ~isempty(commonDir)
319 ldesJar = fullfile(commonDir, 'ldes.jar');
320 javaExe = SolverLDES.getJavaExe();
321 if exist(ldesJar, 'file') == 2 && ~isempty(javaExe)
322 runners{end+1} = sprintf('"%s" -jar "%s"', javaExe, ldesJar);
323 end
324 end
325 end
326
327 function javaExe = getJavaExe()
328 % JAVAEXE = GETJAVAEXE()
329 % Resolve a Java launcher: LINE_JAVA, then JAVA_HOME/bin/java, then
330 % the JRE bundled with MATLAB, then "java" on PATH. Returns '' if none
331 % is found.
332 javaExe = '';
333 exeName = 'java';
334 if ispc
335 exeName = 'java.exe';
336 end
337 cands = {};
338 envJava = getenv('LINE_JAVA');
339 if ~isempty(envJava)
340 cands{end+1} = envJava; %#ok<AGROW>
341 end
342 javaHome = getenv('JAVA_HOME');
343 if ~isempty(javaHome)
344 cands{end+1} = fullfile(javaHome, 'bin', exeName); %#ok<AGROW>
345 end
346 % JRE bundled with MATLAB (layout varies across releases).
347 mlJre = fullfile(matlabroot, 'sys', 'java', 'jre', computer('arch'), 'jre', 'bin', exeName);
348 cands{end+1} = mlJre; %#ok<AGROW>
349 for i = 1:numel(cands)
350 if exist(cands{i}, 'file') == 2
351 javaExe = cands{i};
352 return;
353 end
354 end
355 % Last resort: rely on PATH resolution.
356 [st, ~] = system(sprintf('%s -version', exeName));
357 if st == 0
358 javaExe = exeName;
359 end
360 end
361
362 end
363end
Definition Station.m:245