1classdef SolverLDES < NetworkSolver
2 % SolverLDES LINE Discrete Event Simulator solver
using SSJ library
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.
9 % For LayeredNetwork (LQN) models, SolverLDES also supports LDES simulation.
11 % @brief Discrete-
event simulation solver
using SSJ library
15 % solver = SolverLDES(model,
'samples', 1000000,
'seed', 23000);
16 % solver.getAvg(); % Run LDES simulation
19 % Passing an auxiliary solver as first optional argument warm-starts
the
20 % simulation from that solver's steady-state solution (see initFromSolver):
22 % solver = SolverLDES(model, SolverMVA(model), 'samples', 50000);
25 % Copyright (c) 2012-2026, Imperial College London
26 % All rights reserved.
29 function self = SolverLDES(model, varargin)
30 % SOLVERLDES Create an LDES solver instance
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
37 % Accept a JMT file path in place of a Network object
38 if ischar(model) || isstring(model)
39 model = JMT2LINE(char(model));
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).
45 if ~isempty(varargin) && isa(varargin{1},
'NetworkSolver')
46 initSolver = varargin{1};
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);
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).
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);
72 function sn = getStruct(self)
75 % Get data structure summarizing
the model
76 if isa(self.model,
'LayeredNetwork')
77 sn = self.model.getStruct();
79 sn = self.model.getStruct(true);
83 function [allMethods] = listValidMethods(self)
84 % allMethods = LISTVALIDMETHODS()
85 % List valid methods for this solver
87 allMethods = {
'default'};
90 function
bool = isStochasticMethod(self, method) %#ok<INUSD>
91 % BOOL = ISSTOCHASTICMETHOD(METHOD)
92 % LDES
is a discrete-event simulator; all methods are stochastic.
99 function featSupported = getFeatureSet()
100 % FEATSUPPORTED = GETFEATURESET()
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
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'});
177 function [bool, featSupported] = supports(model)
178 % [BOOL, FEATSUPPORTED] = SUPPORTS(MODEL)
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.
185 featSupported = SolverFeatureSet;
189 % Regular Network support
190 featUsed = model.getUsedLangFeatures();
191 featSupported = SolverLDES.getFeatureSet();
192 bool = SolverFeatureSet.supports(featSupported, featUsed);
195 function options = defaultOptions()
196 % OPTIONS = DEFAULTOPTIONS()
198 options = SolverOptions('LDES');
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.
209 cp = javaclasspath('-all');
214 [pdir, nm, ext] = fileparts(
char(cp{i}));
215 if strcmpi([nm ext],
'jline.jar')
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'));
224 root = fileparts(root);
226 cand = fullfile(root, 'common');
227 if exist(cand, 'dir')
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.
237 fid = fopen(path, 'r');
241 hdr = fread(fid, 20, '*uint8');
246 % Magic: 0x7F 'E' 'L' 'F'
247 if ~(hdr(1) == 127 && hdr(2) == uint8('E') && hdr(3) == uint8('L') && hdr(4) == uint8('F'))
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).
253 m =
double(hdr(19)) +
double(hdr(20)) * 256;
255 m =
double(hdr(19)) * 256 +
double(hdr(20));
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')
266 m = 62; % 0x3E EM_X86_64
268 m = 183; % 0xB7 EM_AARCH64
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.
284 if ~(isunix && ~ismac) % Linux only
287 commonDir = SolverLDES.getLdesCommonDir();
288 if isempty(commonDir)
291 cand = fullfile(commonDir, 'ldes');
292 if exist(cand, 'file') ~= 2
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
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).
313 nativePath = SolverLDES.getLdesNativePath();
314 if ~isempty(nativePath)
315 runners{end+1} = sprintf('"%s"', nativePath);
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);
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
335 exeName =
'java.exe';
338 envJava = getenv(
'LINE_JAVA');
340 cands{end+1} = envJava; %#ok<AGROW>
342 javaHome = getenv(
'JAVA_HOME');
343 if ~isempty(javaHome)
344 cands{end+1} = fullfile(javaHome, 'bin', exeName); %#ok<AGROW>
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
355 % Last resort: rely on PATH resolution.
356 [st, ~] = system(sprintf(
'%s -version', exeName));