LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
JLINE.m
1classdef JLINE
2 % JLINE Conversion utilities for JLINE format models
3 %
4 % JLINE provides static methods to convert between LINE MATLAB models
5 % and JLINE Java/Kotlin models. This class serves as the primary interface
6 % for interoperability between the MATLAB and Java implementations of LINE.
7 %
8 % @brief JLINE format conversion and Java interoperability utilities
9 %
10 % Main functionality:
11 % - Convert LINE MATLAB models to JLINE Java models
12 % - Convert JLINE Java models back to LINE MATLAB format
13 % - Access JLINE solvers from MATLAB
14 % - Handle serialization between MATLAB and Java representations
15 %
16 % Example:
17 % @code
18 % % Convert a LINE model to JLINE format
19 % jnetwork = JLINE.from_model(network);
20 % % Get a JLINE solver
21 % jssa = JLINE.get_solver(jnetwork, 'ssa');
22 % @endcode
23
24 methods(Static)
25
26 function jar_loc = get_jar_location()
27 % Get jline.jar location, downloading if necessary.
28 % Re-checks on each call, so deleted JAR triggers re-download.
29 % Treats lang='java' as a wrapper that auto-downloads jline.jar if absent.
30 jar_loc = which('jline.jar');
31 if isempty(jar_loc) || ~isfile(jar_loc)
32 jar_loc = lineDownloadJAR(false); % Silent re-download
33 end
34 end
35
36 function model = from_line_layered_network(line_layered_network)
37 sn = line_layered_network.getStruct;
38
39 %% initialization
40 model = javaObject('jline.lang.layered.LayeredNetwork', line_layered_network.getName);
41
42 %% host processors
43 P = cell(1,sn.nhosts);
44 for h=1:sn.nhosts
45 if isinf(sn.mult(h))
46 sn_mult_h = java.lang.Integer.MAX_VALUE;
47 else
48 sn_mult_h = sn.mult(h);
49 end
50 switch sn.sched(h)
51 case SchedStrategy.REF
52 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.REF);
53 case SchedStrategy.INF
54 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.INF);
55 case SchedStrategy.FCFS
56 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.FCFS);
57 case SchedStrategy.LCFS
58 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.LCFS);
59 case SchedStrategy.SIRO
60 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.SIRO);
61 case SchedStrategy.SJF
62 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.SJF);
63 case SchedStrategy.LJF
64 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.LJF);
65 case SchedStrategy.PS
66 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.PS);
67 case SchedStrategy.DPS
68 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.DPS);
69 case SchedStrategy.GPS
70 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.GPS);
71 case SchedStrategy.SEPT
72 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.SEPT);
73 case SchedStrategy.LEPT
74 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.LEPT);
75 case {SchedStrategy.HOL, SchedStrategy.FCFSPRIO}
76 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.FCFSPRIO);
77 case SchedStrategy.FORK
78 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.FORK);
79 case SchedStrategy.EXT
80 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.EXT);
81 case SchedStrategy.LCFSPR
82 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.LCFSPR);
83 case SchedStrategy.LCFSPI
84 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.LCFSPI);
85 case SchedStrategy.LCFSPRIO
86 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.LCFSPRIO);
87 case SchedStrategy.LCFSPRPRIO
88 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.LCFSPRPRIO);
89 case SchedStrategy.LCFSPIPRIO
90 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.LCFSPIPRIO);
91 case SchedStrategy.PSPRIO % todo
92 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.PSPRIO);
93 case SchedStrategy.DPSPRIO % todo
94 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.DPSPRIO);
95 case SchedStrategy.GPSPRIO % todo
96 P{h} = javaObject('jline.lang.layered.Processor', model, sn.names{h}, sn_mult_h, jline.lang.constant.SchedStrategy.GPSPRIO);
97 end
98 if sn.repl(h)~=1
99 P{h}.setReplication(sn.repl(h));
100 end
101 end
102
103 %% tasks
104 T = cell(1,sn.ntasks);
105 for t=1:sn.ntasks
106 tidx = sn.tshift+t;
107 if isinf(sn.mult(tidx))
108 sn_mult_tidx = java.lang.Integer.MAX_VALUE;
109 else
110 sn_mult_tidx = sn.mult(tidx);
111 end
112 % Check if this is a CacheTask
113 if sn.iscache(tidx)
114 % Get replacement strategy from ordinal
115 switch sn.replacestrat(tidx)
116 case ReplacementStrategy.RR
117 jReplacestrat = jline.lang.constant.ReplacementStrategy.RR;
118 case ReplacementStrategy.FIFO
119 jReplacestrat = jline.lang.constant.ReplacementStrategy.FIFO;
120 case ReplacementStrategy.SFIFO
121 jReplacestrat = jline.lang.constant.ReplacementStrategy.SFIFO;
122 case ReplacementStrategy.LRU
123 jReplacestrat = jline.lang.constant.ReplacementStrategy.LRU;
124 otherwise
125 jReplacestrat = jline.lang.constant.ReplacementStrategy.FIFO;
126 end
127 T{t} = javaObject('jline.lang.layered.CacheTask', model, sn.names{tidx}, sn.nitems(tidx), sn.itemcap{tidx}, jReplacestrat, sn_mult_tidx);
128 if isfield(sn,'hasretrieval') && numel(sn.hasretrieval)>=tidx && sn.hasretrieval(tidx)
129 T{t}.setRetrieval(true);
130 end
131 elseif sn.isfunction(tidx)
132 % FunctionTask (has setupTime/delayOffTime)
133 jSchedStrategy = JLINE.to_jline_sched_strategy(sn.sched(tidx));
134 T{t} = javaObject('jline.lang.layered.FunctionTask', model, sn.names{tidx}, sn_mult_tidx, jSchedStrategy);
135 else
136 jSchedStrategy = JLINE.to_jline_sched_strategy(sn.sched(tidx));
137 T{t} = javaObject('jline.lang.layered.Task', model, sn.names{tidx}, sn_mult_tidx, jSchedStrategy);
138 end
139 T{t}.on(P{sn.parent(tidx)});
140 if sn.repl(tidx)~=1
141 T{t}.setReplication(sn.repl(tidx));
142 end
143 if ~isempty(sn.think{tidx}) && sn.think_type(tidx) ~= ProcessType.DISABLED
144 switch sn.think_type(tidx)
145 case ProcessType.IMMEDIATE
146 T{t}.setThinkTime(jline.lang.processes.Immediate);
147 case ProcessType.EXP
148 T{t}.setThinkTime(jline.lang.processes.Exp(1/sn.think_mean(tidx)));
149 case ProcessType.ERLANG
150 T{t}.setThinkTime(jline.lang.processes.Erlang.fitMeanAndSCV(sn.think_mean(tidx), sn.think_scv(tidx)));
151 case ProcessType.HYPEREXP
152 if ~isempty(sn.think_params{tidx}) && length(sn.think_params{tidx}) >= 3
153 T{t}.setThinkTime(jline.lang.processes.HyperExp(sn.think_params{tidx}(1), sn.think_params{tidx}(2), sn.think_params{tidx}(3)));
154 else
155 T{t}.setThinkTime(jline.lang.processes.HyperExp.fitMeanAndSCV(sn.think_mean(tidx), sn.think_scv(tidx)));
156 end
157 case ProcessType.COXIAN
158 T{t}.setThinkTime(jline.lang.processes.Coxian.fitMeanAndSCV(sn.think_mean(tidx), sn.think_scv(tidx)));
159 case ProcessType.APH
160 T{t}.setThinkTime(jline.lang.processes.APH.fitMeanAndSCV(sn.think_mean(tidx), sn.think_scv(tidx)));
161 case ProcessType.PH
162 if ~isempty(sn.think_proc{tidx})
163 proc = sn.think_proc{tidx};
164 T{t}.setThinkTime(jline.lang.processes.PH(JLINE.from_line_matrix(proc{1}), JLINE.from_line_matrix(proc{2})));
165 else
166 T{t}.setThinkTime(jline.lang.processes.Exp(1/sn.think_mean(tidx)));
167 end
168 case ProcessType.MAP
169 if ~isempty(sn.think_proc{tidx})
170 proc = sn.think_proc{tidx};
171 T{t}.setThinkTime(jline.lang.processes.MAP(JLINE.from_line_matrix(proc{1}), JLINE.from_line_matrix(proc{2})));
172 else
173 T{t}.setThinkTime(jline.lang.processes.Exp(1/sn.think_mean(tidx)));
174 end
175 case ProcessType.DET
176 T{t}.setThinkTime(jline.lang.processes.Det(sn.think_mean(tidx)));
177 case ProcessType.UNIFORM
178 p = sn.think_params{tidx};
179 T{t}.setThinkTime(jline.lang.processes.Uniform(p(1), p(2)));
180 case ProcessType.GAMMA
181 p = sn.think_params{tidx};
182 T{t}.setThinkTime(jline.lang.processes.Gamma(p(1), p(2)));
183 case ProcessType.PARETO
184 p = sn.think_params{tidx};
185 T{t}.setThinkTime(jline.lang.processes.Pareto(p(1), p(2)));
186 case ProcessType.WEIBULL
187 p = sn.think_params{tidx};
188 T{t}.setThinkTime(jline.lang.processes.Weibull(p(1), p(2)));
189 case ProcessType.LOGNORMAL
190 p = sn.think_params{tidx};
191 T{t}.setThinkTime(jline.lang.processes.Lognormal(p(1), p(2)));
192 otherwise
193 line_error(mfilename,sprintf('JLINE conversion does not support the %s distribution for task think time yet.',char(sn.think_type(tidx))));
194 end
195 end
196 % Setup time. The distribution is rebuilt from its type, mean and
197 % SCV: handing the mean to setSetupTime(double) made the JAR
198 % rebuild it as Exp(1/mean) with SCV 1, so a non-exponential
199 % setup lost its variability crossing into lang='java' while the
200 % mean still matched, and the two langs disagreed silently.
201 if sn.isfunction(tidx) && ~isnan(sn.setuptime_mean(tidx)) && sn.setuptime_mean(tidx) > 1e-8
202 T{t}.setSetupTime(JLINE.from_line_lqn_dist(sn.setuptime_type(tidx), ...
203 sn.setuptime_mean(tidx), sn.setuptime_scv(tidx), ...
204 sn.setuptime_params{tidx}, sn.setuptime_proc{tidx}));
205 end
206 % Delay-off time
207 if sn.isfunction(tidx) && ~isnan(sn.delayofftime_mean(tidx)) && sn.delayofftime_mean(tidx) > 1e-8
208 T{t}.setDelayOffTime(JLINE.from_line_lqn_dist(sn.delayofftime_type(tidx), ...
209 sn.delayofftime_mean(tidx), sn.delayofftime_scv(tidx), ...
210 sn.delayofftime_params{tidx}, sn.delayofftime_proc{tidx}));
211 end
212 end
213 %% entries
214 E = cell(1,sn.nentries);
215 for e=1:sn.nentries
216 eidx = sn.eshift+e;
217 % Check if this is an ItemEntry (has nitems > 0)
218 if sn.nitems(eidx) > 0
219 % ItemEntry requires cardinality and popularity distribution
220 if ~isempty(sn.itemproc) && ~isempty(sn.itemproc{eidx})
221 jPopularity = JLINE.from_line_distribution(sn.itemproc{eidx});
222 else
223 % Default to uniform distribution
224 jPopularity = javaObject('jline.lang.processes.DiscreteSampler', jline.util.matrix.Matrix.uniformDistribution(sn.nitems(eidx)));
225 end
226 E{e} = javaObject('jline.lang.layered.ItemEntry', model, sn.names{eidx}, sn.nitems(eidx), jPopularity);
227 else
228 E{e} = javaObject('jline.lang.layered.Entry', model, sn.names{eidx});
229 end
230 E{e}.on(T{sn.parent(eidx)-sn.tshift});
231 % Open arrival at the entry. The bridge previously never called
232 % setArrival, so open-arrival LQNs lost their source crossing
233 % into lang='java'. Guarded on arrival_mean, which is NaN for
234 % entries without an arrival: arrival_type preallocates to 0,
235 % which collides with ProcessType.EXP.
236 if ~isnan(sn.arrival_mean(eidx)) && sn.arrival_mean(eidx) > 0
237 E{e}.setArrival(JLINE.from_line_lqn_dist(sn.arrival_type(eidx), ...
238 sn.arrival_mean(eidx), sn.arrival_scv(eidx), ...
239 sn.arrival_params{eidx}, sn.arrival_proc{eidx}));
240 end
241 end
242
243 %% activities
244 A = cell(1,sn.nacts);
245 for a=1:sn.nacts
246 aidx = sn.ashift+a;
247 tidx = sn.parent(aidx);
248 onTask = tidx-sn.tshift;
249 % Convert host demand from primitives to Java distribution
250 switch sn.hostdem_type(aidx)
251 case ProcessType.IMMEDIATE
252 jHostDem = jline.lang.processes.Immediate;
253 case ProcessType.DISABLED
254 jHostDem = jline.lang.processes.Disabled;
255 case ProcessType.EXP
256 jHostDem = javaObject('jline.lang.processes.Exp', 1/sn.hostdem_mean(aidx));
257 case ProcessType.ERLANG
258 jHostDem = jline.lang.processes.Erlang.fitMeanAndSCV(sn.hostdem_mean(aidx), sn.hostdem_scv(aidx));
259 case ProcessType.HYPEREXP
260 if ~isempty(sn.hostdem_params{aidx}) && length(sn.hostdem_params{aidx}) >= 3
261 jHostDem = javaObject('jline.lang.processes.HyperExp', sn.hostdem_params{aidx}(1), sn.hostdem_params{aidx}(2), sn.hostdem_params{aidx}(3));
262 else
263 jHostDem = jline.lang.processes.HyperExp.fitMeanAndSCV(sn.hostdem_mean(aidx), sn.hostdem_scv(aidx));
264 end
265 case ProcessType.COXIAN
266 jHostDem = jline.lang.processes.Coxian.fitMeanAndSCV(sn.hostdem_mean(aidx), sn.hostdem_scv(aidx));
267 case ProcessType.APH
268 jHostDem = jline.lang.processes.APH.fitMeanAndSCV(sn.hostdem_mean(aidx), sn.hostdem_scv(aidx));
269 case ProcessType.PH
270 if ~isempty(sn.hostdem_proc{aidx})
271 proc = sn.hostdem_proc{aidx};
272 jHostDem = javaObject('jline.lang.processes.PH', JLINE.from_line_matrix(proc{1}), JLINE.from_line_matrix(proc{2}));
273 else
274 jHostDem = javaObject('jline.lang.processes.Exp', 1/sn.hostdem_mean(aidx));
275 end
276 case ProcessType.MAP
277 if ~isempty(sn.hostdem_proc{aidx})
278 proc = sn.hostdem_proc{aidx};
279 jHostDem = javaObject('jline.lang.processes.MAP', JLINE.from_line_matrix(proc{1}), JLINE.from_line_matrix(proc{2}));
280 else
281 jHostDem = javaObject('jline.lang.processes.Exp', 1/sn.hostdem_mean(aidx));
282 end
283 case ProcessType.DET
284 jHostDem = javaObject('jline.lang.processes.Det', sn.hostdem_mean(aidx));
285 case ProcessType.UNIFORM
286 p = sn.hostdem_params{aidx};
287 jHostDem = javaObject('jline.lang.processes.Uniform', p(1), p(2));
288 case ProcessType.GAMMA
289 p = sn.hostdem_params{aidx};
290 jHostDem = javaObject('jline.lang.processes.Gamma', p(1), p(2));
291 case ProcessType.PARETO
292 p = sn.hostdem_params{aidx};
293 jHostDem = javaObject('jline.lang.processes.Pareto', p(1), p(2));
294 case ProcessType.WEIBULL
295 p = sn.hostdem_params{aidx};
296 jHostDem = javaObject('jline.lang.processes.Weibull', p(1), p(2));
297 case ProcessType.LOGNORMAL
298 p = sn.hostdem_params{aidx};
299 jHostDem = javaObject('jline.lang.processes.Lognormal', p(1), p(2));
300 otherwise
301 line_error(mfilename,sprintf('JLINE conversion does not support the %s distribution for host demand yet.',char(sn.hostdem_type(aidx))));
302 end
303 A{a} = javaObject('jline.lang.layered.Activity', model, sn.names{aidx}, jHostDem);
304 A{a}.on(T{onTask});
305
306 boundTo = find(sn.graph((sn.eshift+1):(sn.eshift+sn.nentries),aidx));
307
308 if ~isempty(boundTo)
309 A{a}.boundTo(E{boundTo});
310 end
311
312 if sn.sched(tidx) ~= SchedStrategy.REF % ref tasks don't reply
313 repliesTo = find(sn.replygraph(a,:)); % index of entry
314 if ~isempty(repliesTo)
315 if ~sn.isref(sn.parent(sn.eshift+repliesTo))
316 A{a}.repliesTo(E{repliesTo});
317 end
318 end
319 end
320
321 if ~isempty(sn.callpair)
322 cidxs = find(sn.callpair(:,1)==aidx);
323 calls = sn.callpair(:,2);
324 for c = cidxs(:)'
325 switch sn.calltype(c)
326 case CallType.SYNC
327 A{a}.synchCall(E{calls(c)-sn.eshift},sn.callproc_mean(c));
328 case CallType.ASYNC
329 A{a}.asynchCall(E{calls(c)-sn.eshift},sn.callproc_mean(c));
330 end
331 end
332 end
333
334 end
335
336 %% think times
337 for h=1:sn.nhosts
338 if ~isempty(sn.think{h}) && sn.think_type(h) ~= ProcessType.DISABLED
339 switch sn.think_type(h)
340 case ProcessType.IMMEDIATE
341 P{h}.setThinkTime(jline.lang.processes.Immediate);
342 case ProcessType.EXP
343 P{h}.setThinkTime(jline.lang.processes.Exp(1/sn.think_mean(h)));
344 case ProcessType.ERLANG
345 P{h}.setThinkTime(jline.lang.processes.Erlang.fitMeanAndSCV(sn.think_mean(h),sn.think_scv(h)));
346 case ProcessType.HYPEREXP
347 % For HyperExp, reconstruct from params if available, otherwise use fitMeanAndSCV
348 if ~isempty(sn.think_params{h}) && length(sn.think_params{h}) >= 3
349 P{h}.setThinkTime(jline.lang.processes.HyperExp(sn.think_params{h}(1), sn.think_params{h}(2), sn.think_params{h}(3)));
350 else
351 P{h}.setThinkTime(jline.lang.processes.HyperExp.fitMeanAndSCV(sn.think_mean(h), sn.think_scv(h)));
352 end
353 case ProcessType.COXIAN
354 % For Coxian, use fitMeanAndSCV
355 P{h}.setThinkTime(jline.lang.processes.Coxian.fitMeanAndSCV(sn.think_mean(h), sn.think_scv(h)));
356 case ProcessType.APH
357 % For APH, reconstruct from params if available
358 if ~isempty(sn.think_params{h})
359 P{h}.setThinkTime(jline.lang.processes.APH.fitMeanAndSCV(sn.think_mean(h), sn.think_scv(h)));
360 else
361 P{h}.setThinkTime(jline.lang.processes.Exp(1/sn.think_mean(h)));
362 end
363 case ProcessType.DET
364 P{h}.setThinkTime(jline.lang.processes.Det(sn.think_mean(h)));
365 case ProcessType.UNIFORM
366 p = sn.think_params{h};
367 P{h}.setThinkTime(jline.lang.processes.Uniform(p(1), p(2)));
368 case ProcessType.GAMMA
369 p = sn.think_params{h};
370 P{h}.setThinkTime(jline.lang.processes.Gamma(p(1), p(2)));
371 case ProcessType.PARETO
372 p = sn.think_params{h};
373 P{h}.setThinkTime(jline.lang.processes.Pareto(p(1), p(2)));
374 case ProcessType.WEIBULL
375 p = sn.think_params{h};
376 P{h}.setThinkTime(jline.lang.processes.Weibull(p(1), p(2)));
377 case ProcessType.LOGNORMAL
378 p = sn.think_params{h};
379 P{h}.setThinkTime(jline.lang.processes.Lognormal(p(1), p(2)));
380 otherwise
381 line_error(mfilename,sprintf('JLINE conversion does not support the %s distribution yet.',char(sn.think_type(h))));
382 end
383 end
384 end
385
386 %% Sequential precedences
387 for ai = 1:sn.nacts
388 aidx = sn.ashift + ai;
389 tidx = sn.parent(aidx);
390 % for all successors
391 for bidx=find(sn.graph(aidx,:))
392 if bidx > sn.ashift % ignore precedence between entries and activities
393 % Serial pattern (SEQ)
394 if full(sn.actpretype(aidx)) == ActivityPrecedenceType.PRE_SEQ && full(sn.actposttype(bidx)) == ActivityPrecedenceType.POST_SEQ
395 T{tidx-sn.tshift}.addPrecedence(jline.lang.layered.ActivityPrecedence.Serial(sn.names{aidx}, sn.names{bidx}));
396 end
397 end
398 end
399 end
400
401 %%%%%%%%%%%%%%%%%%%%%%%%%%% translated up to here
402
403 %% Loop precedences (POST_LOOP)
404 % Loop structure in sn.graph:
405 % - Entry activity (preAct) has edge to first loop body activity
406 % - Last loop body activity has back-edge to first loop body (weight = 1-1/counts)
407 % - Last loop body activity has edge to end activity (weight = 1/counts)
408 % - All loop body and end activities have POST_LOOP type
409 processedLoops = false(1, sn.nacts);
410 for ai = 1:sn.nacts
411 aidx = sn.ashift + ai;
412 tidx = sn.parent(aidx);
413 % Check if this activity starts a loop (has a successor with POST_LOOP type)
414 % and hasn't been processed as part of another loop
415 if processedLoops(ai)
416 continue;
417 end
418
419 successors = find(sn.graph(aidx,:));
420 for bidx = successors
421 if bidx > sn.ashift && full(sn.actposttype(bidx)) == ActivityPrecedenceType.POST_LOOP
422 % Skip if this loop body activity was already processed
423 if processedLoops(bidx - sn.ashift)
424 continue;
425 end
426 % Found start of a loop: aidx is the entry, bidx is first loop body activity
427 loopStart = bidx;
428 precActs = java.util.ArrayList();
429
430 % Follow the chain of POST_LOOP activities
431 curIdx = loopStart;
432 while true
433 precActs.add(sprintf("%s", sn.names{curIdx}));
434 processedLoops(curIdx - sn.ashift) = true;
435
436 % Find successors of current activity
437 curSuccessors = find(sn.graph(curIdx,:));
438 curSuccessors = curSuccessors(curSuccessors > sn.ashift);
439
440 % Check for loop termination: find the end activity
441 % End activity has weight = 1/counts (not the back-edge weight)
442 endIdx = 0;
443 nextIdx = 0;
444 for succIdx = curSuccessors
445 if full(sn.actposttype(succIdx)) == ActivityPrecedenceType.POST_LOOP
446 if succIdx == loopStart
447 % This is the back-edge, skip it
448 continue;
449 end
450 weight = full(sn.graph(curIdx, succIdx));
451 if weight > 0 && weight < 1
452 % This is the end activity (weight = 1/counts)
453 endIdx = succIdx;
454 else
455 % This is the next activity in the loop body (weight = 1.0)
456 nextIdx = succIdx;
457 end
458 end
459 end
460
461 if endIdx > 0
462 % Found end activity - calculate counts and output
463 weight = full(sn.graph(curIdx, endIdx));
464 if weight > 0
465 counts = 1/weight;
466 else
467 counts = 1; % Fallback to prevent division by zero
468 end
469 precActs.add(sprintf("%s", sn.names{endIdx}));
470 processedLoops(endIdx - sn.ashift) = true;
471
472 T{tidx-sn.tshift}.addPrecedence(jline.lang.layered.ActivityPrecedence.Loop(sn.names{aidx}, precActs, jline.util.matrix.Matrix(counts)));
473 break;
474 elseif nextIdx > 0
475 % Continue to next activity in loop body
476 curIdx = nextIdx;
477 else
478 % No more successors - shouldn't happen in valid loop
479 break;
480 end
481 end
482 break; % Only process one loop starting from this activity
483 end
484 end
485 end
486
487 %% OrFork precedences (POST_OR)
488 precMarker = 0;
489 for ai = 1:sn.nacts
490 probs = [];
491 aidx = sn.ashift + ai;
492 tidx = sn.parent(aidx);
493 prob_ctr = 0;
494 % for all successors
495 for bidx=find(sn.graph(aidx,:))
496 if bidx > sn.ashift % ignore precedence between entries and activities
497 % Or pattern (POST_OR)
498 if full(sn.actposttype(bidx)) == ActivityPrecedenceType.POST_OR
499 if precMarker == 0 % start a new orjoin
500 precActs = java.util.ArrayList();
501 precMarker = aidx-sn.ashift;
502 precActs.add(sprintf("%s", sn.names{bidx}));
503 probs=full(sn.graph(aidx,bidx));
504 else
505 precActs.add(sprintf("%s", sn.names{bidx}));
506 probs(end+1)=full(sn.graph(aidx,bidx));
507 end
508 end
509 end
510 end
511
512
513 if precMarker > 0
514 probsMatrix = jline.util.matrix.Matrix(1,length(probs));
515 for i=1:length(probs)
516 probsMatrix.set(0,i-1,probs(i));
517 end
518 T{tidx-sn.tshift}.addPrecedence(jline.lang.layered.ActivityPrecedence.OrFork(sn.names{precMarker+sn.ashift}, precActs, probsMatrix));
519 precMarker = 0;
520 end
521 end
522
523 %% AndFork precedences (POST_AND)
524 precMarker = 0;
525 postActs = '';
526 for ai = 1:sn.nacts
527 aidx = sn.ashift + ai;
528 tidx = sn.parent(aidx);
529 % for all successors
530 for bidx=find(sn.graph(aidx,:))
531 if bidx > sn.ashift % ignore precedence between entries and activities
532 % Or pattern (POST_AND)
533 if full(sn.actposttype(bidx)) == ActivityPrecedenceType.POST_AND
534 if isempty(postActs)
535 postActs = java.util.ArrayList();
536 postActs.add(sprintf("%s", sn.names{bidx}));
537 else
538 postActs.add(sprintf("%s", sn.names{bidx}));
539 end
540
541 if precMarker == 0 % start a new orjoin
542 precMarker = aidx-sn.ashift;
543 end
544 end
545 end
546 end
547 if precMarker > 0
548 T{tidx-sn.tshift}.addPrecedence(jline.lang.layered.ActivityPrecedence.AndFork(sn.names{precMarker+sn.ashift}, postActs));
549 precMarker = 0;
550 end
551 end
552
553 %% CacheAccess precedences (POST_CACHE)
554 precMarker = 0;
555 postActs = '';
556 for ai = 1:sn.nacts
557 aidx = sn.ashift + ai;
558 tidx = sn.parent(aidx);
559 % for all successors
560 for bidx=find(sn.graph(aidx,:))
561 if bidx > sn.ashift % ignore precedence between entries and activities
562 % CacheAccess pattern (POST_CACHE)
563 if full(sn.actposttype(bidx)) == ActivityPrecedenceType.POST_CACHE
564 if isempty(postActs)
565 postActs = java.util.ArrayList();
566 postActs.add(sprintf("%s", sn.names{bidx}));
567 else
568 postActs.add(sprintf("%s", sn.names{bidx}));
569 end
570
571 if precMarker == 0 % start a new cache access
572 precMarker = aidx-sn.ashift;
573 end
574 end
575 end
576 end
577 if precMarker > 0
578 T{tidx-sn.tshift}.addPrecedence(jline.lang.layered.ActivityPrecedence.CacheAccess(sn.names{precMarker+sn.ashift}, postActs));
579 precMarker = 0;
580 postActs = '';
581 end
582 end
583
584 %% OrJoin precedences (PRE_OR)
585 precMarker = 0;
586 for bi = sn.nacts:-1:1
587 bidx = sn.ashift + bi;
588 tidx = sn.parent(bidx);
589 % for all predecessors
590 for aidx=find(sn.graph(:,bidx))'
591 if aidx > sn.ashift % ignore precedence between entries and activities
592 % OrJoin pattern (PRE_OR)
593 if full(sn.actpretype(aidx)) == ActivityPrecedenceType.PRE_OR
594 if precMarker == 0 % start a new orjoin
595 precActs = java.util.ArrayList();
596 precMarker = bidx-sn.ashift;
597 precActs.add(sprintf("%s", sn.names{aidx}));
598 else
599 precActs.add(sprintf("%s", sn.names{aidx}));
600 end
601 end
602 end
603 end
604 if precMarker > 0
605 T{tidx-sn.tshift}.addPrecedence(jline.lang.layered.ActivityPrecedence.OrJoin(precActs, sn.names{precMarker+sn.ashift}));
606 precMarker = 0;
607 end
608 end
609
610 %% AndJoin precedences (PRE_AND)
611 precMarker = 0;
612 for bi = sn.nacts:-1:1
613 bidx = sn.ashift + bi;
614 tidx = sn.parent(bidx);
615 % for all predecessors
616 for aidx=find(sn.graph(:,bidx))'
617 if aidx > sn.ashift % ignore precedence between entries and activities
618 % OrJoin pattern (PRE_AND)
619 if full(sn.actpretype(aidx)) == ActivityPrecedenceType.PRE_AND
620 if precMarker == 0 % start a new orjoin
621 precActs = java.util.ArrayList();
622 precMarker = bidx-sn.ashift;
623 precActs.add(sprintf("%s", sn.names{aidx}));
624 else
625 precActs.add(sprintf("%s", sn.names{aidx}));
626 end
627 end
628 end
629 end
630 if precMarker > 0
631 % Find quorum parameter from original precedence structure
632 postActName = sn.names{precMarker+sn.ashift};
633 localTaskIdx = tidx - sn.tshift;
634 quorum = [];
635 for ap = 1:length(line_layered_network.tasks{localTaskIdx}.precedences)
636 precedence = line_layered_network.tasks{localTaskIdx}.precedences(ap);
637 if precedence.preType == ActivityPrecedenceType.PRE_AND
638 % Check if this precedence contains our post activity
639 if any(strcmp(precedence.postActs, postActName))
640 quorum = precedence.preParams;
641 break;
642 end
643 end
644 end
645
646 if isempty(quorum)
647 T{tidx-sn.tshift}.addPrecedence(jline.lang.layered.ActivityPrecedence.AndJoin(precActs, sn.names{precMarker+sn.ashift}));
648 else
649 T{tidx-sn.tshift}.addPrecedence(jline.lang.layered.ActivityPrecedence.AndJoin(precActs, sn.names{precMarker+sn.ashift}, quorum));
650 end
651 precMarker = 0;
652 end
653 end
654
655 end
656
657 function jdist = from_line_distribution(line_dist)
658 if isa(line_dist, 'Exp')
659 jdist = javaObject('jline.lang.processes.Exp', line_dist.getParam(1).paramValue);
660 elseif isa(line_dist, "APH")
661 alpha = line_dist.getParam(1).paramValue;
662 T = line_dist.getParam(2).paramValue;
663 jline_alpha = java.util.ArrayList();
664 for i = 1:length(alpha)
665 jline_alpha.add(alpha(i));
666 end
667 jline_T = JLINE.from_line_matrix(T);
668 jdist = javaObject('jline.lang.processes.APH', jline_alpha, jline_T);
669 elseif isa(line_dist, 'Coxian')
670 jline_mu = java.util.ArrayList();
671 jline_phi = java.util.ArrayList();
672 if length(line_dist.params) == 3
673 jline_mu.add(line_dist.getParam(1).paramValue);
674 jline_mu.add(line_dist.getParam(2).paramValue);
675 jline_phi.add(line_dist.getParam(3).paramValue);
676 else
677 mu = line_dist.getParam(1).paramValue;
678 phi = line_dist.getParam(2).paramValue;
679 for i = 1:length(mu)
680 jline_mu.add(mu(i));
681 end
682 for i = 1:length(phi)
683 jline_phi.add(phi(i));
684 end
685 end
686 jdist = javaObject('jline.lang.processes.Coxian', jline_mu, jline_phi);
687 elseif isa(line_dist, 'Det')
688 jdist = javaObject('jline.lang.processes.Det', line_dist.getParam(1).paramValue);
689 elseif isa(line_dist, 'DiscreteSampler')
690 popularity_p = JLINE.from_line_matrix(line_dist.getParam(1).paramValue);
691 popularity_val = JLINE.from_line_matrix(line_dist.getParam(2).paramValue);
692 jdist = javaObject('jline.lang.processes.DiscreteSampler', popularity_p, popularity_val);
693 elseif isa(line_dist, 'Erlang')
694 jdist = javaObject('jline.lang.processes.Erlang', line_dist.getParam(1).paramValue, line_dist.getParam(2).paramValue);
695 elseif isa(line_dist, 'Gamma')
696 jdist = javaObject('jline.lang.processes.Gamma', line_dist.getParam(1).paramValue, line_dist.getParam(2).paramValue);
697 elseif isa(line_dist, "HyperExp")
698 jdist = javaObject('jline.lang.processes.HyperExp', line_dist.getParam(1).paramValue, line_dist.getParam(2).paramValue, line_dist.getParam(3).paramValue);
699 elseif isa(line_dist, 'Lognormal')
700 jdist = javaObject('jline.lang.processes.Lognormal', line_dist.getParam(1).paramValue, line_dist.getParam(2).paramValue);
701 elseif isa(line_dist, 'Pareto')
702 jdist = javaObject('jline.lang.processes.Pareto', line_dist.getParam(1).paramValue, line_dist.getParam(2).paramValue);
703 elseif isa(line_dist, 'MAP')
704 D0 = line_dist.D(0);
705 D1 = line_dist.D(1);
706 jdist = javaObject('jline.lang.processes.MAP', JLINE.from_line_matrix(D0), JLINE.from_line_matrix(D1));
707 elseif isa(line_dist, 'MMPP2')
708 lambda0 = line_dist.getParam(1).paramValue;
709 lambda1 = line_dist.getParam(2).paramValue;
710 sigma0 = line_dist.getParam(3).paramValue;
711 sigma1 = line_dist.getParam(4).paramValue;
712 jdist = javaObject('jline.lang.processes.MMPP2', lambda0, lambda1, sigma0, sigma1);
713 elseif isa(line_dist, 'NHPP')
714 jdist = javaObject('jline.lang.processes.NHPP', line_dist.getBreakpoints(), line_dist.getRates(), logical(line_dist.isCyclic()));
715 elseif isa(line_dist, 'BMAP') % before MarkedMAP (BMAP < MarkedMAP)
716 % Both sides store the process cell in MarkedMAP layout
717 % {D0, D1_total, D1..DK}: MATLAB in line_dist.process (params
718 % are unreliable, see the constructor note in BMAP.m) and the
719 % JAR BMAP(MatrixCell) constructor reads index 1 as D1_total
720 % (see its validateGenerator), so the cell is passed as-is
721 nmp = length(line_dist.process);
722 jD = javaArray('jline.util.matrix.Matrix', nmp);
723 for k = 1:nmp
724 jD(k) = JLINE.from_line_matrix(line_dist.process{k});
725 end
726 jdist = javaObject('jline.lang.processes.BMAP', javaObject('jline.util.matrix.MatrixCell', jD));
727 elseif isa(line_dist, 'MarkedMMPP')
728 nmp = length(line_dist.params); % {D0, D1, D11..D1K}
729 jD = javaArray('jline.util.matrix.Matrix', nmp);
730 for k = 1:nmp
731 jD(k) = JLINE.from_line_matrix(line_dist.getParam(k).paramValue);
732 end
733 jdist = javaObject('jline.lang.processes.MarkedMMPP', javaObject('jline.util.matrix.MatrixCell', jD));
734 elseif isa(line_dist, 'MarkedMAP')
735 nmp = length(line_dist.params); % {D0, D1, D11..D1K}
736 jD = javaArray('jline.util.matrix.Matrix', nmp);
737 for k = 1:nmp
738 jD(k) = JLINE.from_line_matrix(line_dist.getParam(k).paramValue);
739 end
740 jdist = javaObject('jline.lang.processes.MarkedMAP', javaObject('jline.util.matrix.MatrixCell', jD));
741 elseif isa(line_dist, 'DMAP')
742 jdist = javaObject('jline.lang.processes.DMAP', javaObject('jline.util.matrix.MatrixCell', JLINE.from_line_matrix(line_dist.getParam(1).paramValue), JLINE.from_line_matrix(line_dist.getParam(2).paramValue)));
743 elseif isa(line_dist, 'ME')
744 jdist = javaObject('jline.lang.processes.ME', JLINE.from_line_matrix(line_dist.getParam(1).paramValue), JLINE.from_line_matrix(line_dist.getParam(2).paramValue));
745 elseif isa(line_dist, 'RAP')
746 jdist = javaObject('jline.lang.processes.RAP', JLINE.from_line_matrix(line_dist.getParam(1).paramValue), JLINE.from_line_matrix(line_dist.getParam(2).paramValue));
747 elseif isa(line_dist, 'MMDP2') % before MMDP (MMDP2 < MMDP)
748 jdist = javaObject('jline.lang.processes.MMDP2', line_dist.getParam(1).paramValue, line_dist.getParam(2).paramValue, line_dist.getParam(3).paramValue, line_dist.getParam(4).paramValue);
749 elseif isa(line_dist, 'MMDP')
750 jdist = javaObject('jline.lang.processes.MMDP', JLINE.from_line_matrix(line_dist.getParam(1).paramValue), JLINE.from_line_matrix(line_dist.getParam(2).paramValue));
751 elseif isa(line_dist, 'Normal')
752 jdist = javaObject('jline.lang.processes.Normal', line_dist.getParam(1).paramValue, line_dist.getParam(2).paramValue);
753 elseif isa(line_dist, 'Bernoulli')
754 jdist = javaObject('jline.lang.processes.Bernoulli', line_dist.getParam(1).paramValue);
755 elseif isa(line_dist, 'Geometric')
756 jdist = javaObject('jline.lang.processes.Geometric', line_dist.getParam(1).paramValue);
757 elseif isa(line_dist, 'Poisson')
758 jdist = javaObject('jline.lang.processes.Poisson', line_dist.getParam(1).paramValue);
759 elseif isa(line_dist, 'Binomial')
760 jdist = javaObject('jline.lang.processes.Binomial', int32(line_dist.getParam(1).paramValue), line_dist.getParam(2).paramValue);
761 elseif isa(line_dist, 'DiscreteUniform')
762 jdist = javaObject('jline.lang.processes.DiscreteUniform', line_dist.getParam(1).paramValue, line_dist.getParam(2).paramValue);
763 elseif isa(line_dist, 'EmpiricalCDF')
764 % MATLAB stores data as [cdf, x] column pairs; the JAR two-argument
765 % constructor takes (cdfdata, xdata)
766 if size(line_dist.data, 2) >= 2
767 jdist = javaObject('jline.lang.processes.EmpiricalCDF', JLINE.from_line_matrix(line_dist.data(:,1)), JLINE.from_line_matrix(line_dist.data(:,2)));
768 else
769 jdist = javaObject('jline.lang.processes.EmpiricalCDF', JLINE.from_line_matrix(line_dist.data));
770 end
771 elseif isa(line_dist, 'PH')
772 alpha = line_dist.getParam(1).paramValue;
773 T = line_dist.getParam(2).paramValue;
774 jdist = javaObject('jline.lang.processes.PH', JLINE.from_line_matrix(alpha), JLINE.from_line_matrix(T));
775 elseif isa(line_dist, 'Uniform')
776 jdist = javaObject('jline.lang.processes.Uniform', line_dist.getParam(1).paramValue, line_dist.getParam(2).paramValue);
777 elseif isa(line_dist, 'Weibull')
778 jdist = javaObject('jline.lang.processes.Weibull', line_dist.getParam(1).paramValue, line_dist.getParam(2).paramValue);
779 elseif isa(line_dist, 'Zipf')
780 jdist = javaObject('jline.lang.processes.Zipf', line_dist.getParam(3).paramValue, line_dist.getParam(4).paramValue);
781 elseif isa(line_dist, 'Immediate')
782 jdist = javaObject('jline.lang.processes.Immediate');
783 elseif isempty(line_dist) || isa(line_dist, 'Disabled')
784 jdist = javaObject('jline.lang.processes.Disabled');
785 return;
786 elseif isa(line_dist, 'Trace') % before Replayer (Trace < Replayer)
787 jdist = javaObject('jline.lang.processes.Trace', line_dist.params{1}.paramValue);
788 elseif isa(line_dist, 'Replayer')
789 jdist = javaObject('jline.lang.processes.Replayer', line_dist.params{1}.paramValue);
790 elseif isa(line_dist, 'Prior')
791 % Convert Prior: each alternative is a distribution
792 dists = line_dist.getParam(1).paramValue;
793 probs = line_dist.getParam(2).paramValue;
794 jdists = java.util.ArrayList();
795 for k = 1:length(dists)
796 jdists.add(JLINE.from_line_distribution(dists{k}));
797 end
798 jdist = javaObject('jline.lang.processes.Prior', jdists, probs);
799 else
800 line_error(mfilename,'Distribution not supported by JLINE.');
801 end
802 end
803
804 function [jRemDist, jRemPol] = from_line_signal_removal(line_class)
805 % FROM_LINE_SIGNAL_REMOVAL Marshal a signal class's batch-removal
806 % distribution and removal policy. Returns empties when the class
807 % uses the defaults (remove exactly 1, RANDOM policy), so callers
808 % can keep using the short JAR constructors in that case.
809 jRemDist = [];
810 jRemPol = [];
811 if isprop(line_class, 'removalDistribution') && ~isempty(line_class.removalDistribution) ...
812 && ~isa(line_class.removalDistribution, 'Disabled')
813 jRemDist = JLINE.from_line_distribution(line_class.removalDistribution);
814 end
815 if isprop(line_class, 'removalPolicy') && ~isempty(line_class.removalPolicy)
816 if ~isempty(jRemDist) || line_class.removalPolicy ~= RemovalPolicy.RANDOM
817 jRemPol = jline.lang.constant.RemovalPolicy.fromID(int32(line_class.removalPolicy));
818 end
819 elseif ~isempty(jRemDist)
820 jRemPol = jline.lang.constant.RemovalPolicy.RANDOM;
821 end
822 end
823
824 function matlab_dist = from_jline_distribution(jdist)
825 if isa(jdist, 'jline.lang.processes.Exp')
826 matlab_dist = Exp(jdist.getRate());
827 elseif isa(jdist, 'jline.lang.processes.Det')
828 matlab_dist = Det(jdist.getParam(1).getValue);
829 elseif isa(jdist, 'jline.lang.processes.Erlang')
830 matlab_dist = Erlang(jdist.getParam(1).getValue(),jdist.getNumberOfPhases());
831 elseif isa(jdist, 'jline.lang.processes.Gamma')
832 matlab_dist = Gamma(jdist.getParam(1).getValue,jdist.getParam(2).getValue);
833 elseif isa(jdist, 'jline.lang.processes.HyperExp')
834 matlab_dist = HyperExp(jdist.getParam(1).getValue, jdist.getParam(2).getValue, jdist.getParam(3).getValue);
835 elseif isa(jdist, 'jline.lang.processes.Lognormal')
836 matlab_dist = Lognormal(jdist.getParam(1).getValue,jdist.getParam(2).getValue);
837 elseif isa(jdist, 'jline.lang.processes.Pareto')
838 matlab_dist = Pareto(jdist.getParam(1).getValue,jdist.getParam(2).getValue);
839 elseif isa(jdist, 'jline.lang.processes.Uniform')
840 matlab_dist = Uniform(jdist.getParam(1).getValue,jdist.getParam(2).getValue);
841 elseif isa(jdist, 'jline.lang.processes.Weibull')
842 matlab_dist = Weibull(jdist.getParam(1).getValue,jdist.getParam(2).getValue);
843 elseif isa(jdist, 'jline.lang.processes.MAP')
844 D0 = JLINE.from_jline_matrix(jdist.D(0));
845 D1 = JLINE.from_jline_matrix(jdist.D(1));
846 matlab_dist = MAP({D0, D1});
847 elseif isa(jdist, 'jline.lang.processes.APH')
848 alpha = JLINE.from_jline_matrix(jdist.getInitProb());
849 T = JLINE.from_jline_matrix(jdist.getSubgenerator());
850 matlab_dist = APH(alpha(:)', T);
851 elseif isa(jdist, 'jline.lang.processes.PH')
852 alpha = JLINE.from_jline_matrix(jdist.getInitProb());
853 T = JLINE.from_jline_matrix(jdist.getSubgenerator());
854 matlab_dist = PH(alpha(:)', T);
855 elseif isa(jdist, 'jline.lang.processes.Coxian')
856 if jdist.getNumberOfPhases == 2
857 matlab_dist = Coxian([jdist.getParam(1).getValue.get(0), jdist.getParam(1).getValue.get(1)], [jdist.getParam(2).getValue.get(0),1]);
858 else
859 jmu = jdist.getParam(1).getValue;
860 jphi = jdist.getParam(2).getValue;
861 mu = zeros(1, jmu.size);
862 phi = zeros(1, jphi.size);
863 for i = 1:jmu.size
864 mu(i) = jmu.get(i-1);
865 end
866 for i = 1:jphi.size
867 phi(i) = jphi.get(i-1);
868 end
869 matlab_dist = Coxian(mu, phi);
870 end
871 elseif isa(jdist, 'jline.lang.processes.Zipf')
872 matlab_dist = Zipf(jdist.getParam(3).getValue, jdist.getParam(4).getValue);
873 elseif isa(jdist, 'jline.lang.processes.DiscreteSampler')
874 jpMat = jdist.getParam(1).getValue;
875 jxMat = jdist.getParam(2).getValue;
876 p = zeros(1, jpMat.length);
877 x = zeros(1, jxMat.length);
878 for i = 1:jpMat.length
879 p(i) = jpMat.get(i-1);
880 end
881 for i = 1:jxMat.length
882 x(i) = jxMat.get(i-1);
883 end
884 matlab_dist = DiscreteSampler(p, x);
885 elseif isa(jdist, 'jline.lang.processes.Immediate')
886 matlab_dist = Immediate();
887 elseif isa(jdist, 'jline.lang.processes.Disabled')
888 matlab_dist = Disabled();
889 elseif isa(jdist, 'jline.lang.processes.MMPP2')
890 matlab_dist = MMPP2(jdist.getParam(1).getValue, jdist.getParam(2).getValue, jdist.getParam(3).getValue, jdist.getParam(4).getValue);
891 elseif isa(jdist, 'jline.lang.processes.NHPP')
892 jBp = jdist.getBreakpoints();
893 jRates = jdist.getRates();
894 bp = zeros(1, length(jBp));
895 rates = zeros(1, length(jRates));
896 for k = 1:length(jBp)
897 bp(k) = jBp(k);
898 end
899 for k = 1:length(jRates)
900 rates(k) = jRates(k);
901 end
902 matlab_dist = NHPP(bp, rates, logical(jdist.isCyclic()));
903 elseif isa(jdist, 'jline.lang.processes.Prior')
904 % Convert Prior from JAR to MATLAB
905 jdists = jdist.getDistributions();
906 nalt = jdists.size();
907 dists = cell(1, nalt);
908 for k = 1:nalt
909 dists{k} = JLINE.from_jline_distribution(jdists.get(k-1));
910 end
911 probs = jdist.getProbabilities();
912 matlab_dist = Prior(dists, probs);
913 elseif isa(jdist, 'jline.lang.processes.Normal')
914 matlab_dist = Normal(jdist.getParam(1).getValue, jdist.getParam(2).getValue);
915 elseif isa(jdist, 'jline.lang.processes.Bernoulli')
916 matlab_dist = Bernoulli(jdist.getParam(1).getValue);
917 elseif isa(jdist, 'jline.lang.processes.Geometric')
918 matlab_dist = Geometric(jdist.getParam(1).getValue);
919 elseif isa(jdist, 'jline.lang.processes.Poisson')
920 matlab_dist = Poisson(jdist.getParam(1).getValue);
921 elseif isa(jdist, 'jline.lang.processes.Binomial')
922 matlab_dist = Binomial(double(jdist.getParam(1).getValue), jdist.getParam(2).getValue);
923 elseif isa(jdist, 'jline.lang.processes.DiscreteUniform')
924 matlab_dist = DiscreteUniform(jdist.getParam(1).getValue, jdist.getParam(2).getValue);
925 elseif isa(jdist, 'jline.lang.processes.EmpiricalCDF')
926 d = JLINE.from_jline_matrix(jdist.getData());
927 if size(d, 2) >= 2 % stored as [cdf, x]; MATLAB ctor is (xdata, cdfdata)
928 matlab_dist = EmpiricalCDF(d(:,2), d(:,1));
929 else
930 matlab_dist = EmpiricalCDF(d);
931 end
932 elseif isa(jdist, 'jline.lang.processes.Trace') % before Replayer (Trace < Replayer)
933 v = jdist.getParam(1).getValue;
934 if isa(v, 'java.lang.String'), v = char(v); end
935 matlab_dist = Trace(v);
936 elseif isa(jdist, 'jline.lang.processes.Replayer')
937 v = jdist.getParam(1).getValue;
938 if isa(v, 'java.lang.String'), v = char(v); end
939 matlab_dist = Replayer(v);
940 elseif isa(jdist, 'jline.lang.processes.DMAP')
941 matlab_dist = DMAP(JLINE.from_jline_matrix(jdist.getParam(1).getValue), JLINE.from_jline_matrix(jdist.getParam(2).getValue));
942 elseif isa(jdist, 'jline.lang.processes.ME')
943 matlab_dist = ME(JLINE.from_jline_matrix(jdist.getParam(1).getValue), JLINE.from_jline_matrix(jdist.getParam(2).getValue));
944 elseif isa(jdist, 'jline.lang.processes.RAP')
945 matlab_dist = RAP(JLINE.from_jline_matrix(jdist.getParam(1).getValue), JLINE.from_jline_matrix(jdist.getParam(2).getValue));
946 elseif isa(jdist, 'jline.lang.processes.MMDP2') % before MMDP (MMDP2 < MMDP)
947 matlab_dist = MMDP2(jdist.getParam(1).getValue, jdist.getParam(2).getValue, jdist.getParam(3).getValue, jdist.getParam(4).getValue);
948 elseif isa(jdist, 'jline.lang.processes.MMDP')
949 matlab_dist = MMDP(JLINE.from_jline_matrix(jdist.getParam(1).getValue), JLINE.from_jline_matrix(jdist.getParam(2).getValue));
950 elseif isa(jdist, 'jline.lang.processes.BMAP') % before MarkedMAP (BMAP < MarkedMAP)
951 % The JAR BMAP process cell is in MarkedMAP layout
952 % {D0, D1_total, D1..DK}; the MATLAB BMAP constructor expects
953 % the standard {D0, D1, ..., DK} and rebuilds D1_total itself
954 proc = jdist.getProcess();
955 K = proc.size() - 2;
956 D = cell(1, K+1);
957 D{1} = JLINE.from_jline_matrix(proc.get(0));
958 for k = 1:K
959 D{1+k} = JLINE.from_jline_matrix(proc.get(1+k));
960 end
961 matlab_dist = BMAP(D);
962 elseif isa(jdist, 'jline.lang.processes.MarkedMMPP')
963 proc = jdist.getProcess(); % {D0, D1, D11..D1K}
964 K = proc.size() - 2;
965 D = cell(1, K+2);
966 for k = 1:K+2
967 D{k} = JLINE.from_jline_matrix(proc.get(k-1));
968 end
969 matlab_dist = MarkedMMPP(D, K);
970 elseif isa(jdist, 'jline.lang.processes.MarkedMAP')
971 proc = jdist.getProcess(); % {D0, D1, D11..D1K}
972 K = proc.size() - 2;
973 D = cell(1, K+2);
974 for k = 1:K+2
975 D{k} = JLINE.from_jline_matrix(proc.get(k-1));
976 end
977 matlab_dist = MarkedMAP(D, K);
978 else
979 line_error(mfilename,'Distribution not supported by JLINE.');
980 end
981 end
982
983 function set_csMatrix(line_node, jnode, jclasses)
984 nClasses = length(line_node.model.classes);
985 csMatrix = jnode.initClassSwitchMatrix();
986 for i = 1:nClasses
987 for j = 1:nClasses
988 csMatrix.set(jclasses{i}, jclasses{j}, line_node.server.csFun(i,j,0,0));
989 end
990 end
991 jnode.setClassSwitchingMatrix(csMatrix);
992 end
993
994 function set_service(line_node, jnode, job_classes)
995 if (isa(line_node, 'Sink') || isa(line_node, 'Router') || isa(line_node, 'Cache') || isa(line_node, 'Logger') || isa(line_node, 'ClassSwitch') || isa(line_node, 'Fork') || isa(line_node, 'Join') || isa(line_node, 'Place') || isa(line_node, 'Transition'))
996 return;
997 end
998
999 for n = 1 : length(job_classes)
1000 if (isa(line_node, 'Queue') || isa(line_node, 'Delay'))
1001 matlab_dist = line_node.getService(job_classes{n});
1002 elseif (isa(line_node, 'Source'))
1003 matlab_dist = line_node.getArrivalProcess(job_classes{n});
1004 else
1005 line_error(mfilename,'Node not supported by JLINE.');
1006 end
1007 service_dist = JLINE.from_line_distribution(matlab_dist);
1008
1009 if (isa(line_node,'Queue') || isa(line_node, 'Delay'))
1010 jnode.setService(jnode.getModel().getClasses().get(n-1), service_dist, line_node.schedStrategyPar(n));
1011 elseif (isa(line_node, 'Source'))
1012 jnode.setArrival(jnode.getModel().getClasses().get(n-1), service_dist);
1013 end
1014 end
1015 end
1016
1017 function set_delayoff(line_node, jnode, job_classes)
1018 % Transfer setup and delayoff times from MATLAB Queue to Java Queue
1019 if ~isa(line_node, 'Queue')
1020 return;
1021 end
1022
1023 % Check if setupTime property exists and is not empty
1024 if ~isprop(line_node, 'setupTime') || isempty(line_node.setupTime)
1025 return;
1026 end
1027
1028 for n = 1 : length(job_classes)
1029 c = job_classes{n}.index;
1030 % Check if both setupTime and delayoffTime are set for this class
1031 if c <= length(line_node.setupTime) && ~isempty(line_node.setupTime{1, c}) && ...
1032 c <= length(line_node.delayoffTime) && ~isempty(line_node.delayoffTime{1, c})
1033 % Convert MATLAB distributions to Java distributions
1034 setup_dist = JLINE.from_line_distribution(line_node.setupTime{1, c});
1035 delayoff_dist = JLINE.from_line_distribution(line_node.delayoffTime{1, c});
1036 % Set delayoff on the Java Queue
1037 jnode.setDelayOff(jnode.getModel().getClasses().get(n-1), setup_dist, delayoff_dist);
1038 end
1039 end
1040 end
1041
1042 function set_line_service(jline_node, line_node, job_classes, line_classes)
1043 if (isa(line_node,'Sink')) || isa(line_node, 'ClassSwitch') || isa(line_node, 'Fork') || isa(line_node, 'Join') || isa(line_node, 'Place') || isa(line_node, 'Transition') || isa(line_node, 'Cache')
1044 return;
1045 end
1046 for n = 1:job_classes.size()
1047 if (isa(line_node, 'Queue') || isa(line_node, 'Delay'))
1048 jdist = jline_node.getServiceProcess(job_classes.get(n-1));
1049 matlab_dist = JLINE.from_jline_distribution(jdist);
1050 weight = jline_node.getSchedStrategyPar(job_classes.get(n-1));
1051 line_node.setService(line_classes{n}, matlab_dist, weight);
1052 elseif (isa(line_node, 'Source'))
1053 jdist = jline_node.getArrivalProcess(job_classes.get(n-1));
1054 matlab_dist = JLINE.from_jline_distribution(jdist);
1055 line_node.setArrival(line_classes{n}, matlab_dist);
1056 elseif (isa(line_node, 'Router'))
1057 % no-op
1058 else
1059 line_error(mfilename,'Node not supported by JLINE.');
1060 end
1061 end
1062 end
1063
1064 function node_object = from_line_node(line_node, jnetwork, ~, forkNode, sn)
1065 % Handle optional sn argument
1066 if nargin < 5
1067 sn = [];
1068 end
1069 if isa(line_node, 'Delay')
1070 node_object = javaObject('jline.lang.nodes.Delay', jnetwork, line_node.getName);
1071 elseif isa(line_node, 'Queue')
1072 switch line_node.schedStrategy
1073 case SchedStrategy.INF
1074 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.INF);
1075 case SchedStrategy.FCFS
1076 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.FCFS);
1077 case SchedStrategy.LCFS
1078 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.LCFS);
1079 case SchedStrategy.SIRO
1080 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.SIRO);
1081 case SchedStrategy.SJF
1082 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.SJF);
1083 case SchedStrategy.LJF
1084 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.LJF);
1085 case SchedStrategy.PS
1086 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.PS);
1087 case SchedStrategy.DPS
1088 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.DPS);
1089 case SchedStrategy.GPS
1090 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.GPS);
1091 case SchedStrategy.SEPT
1092 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.SEPT);
1093 case SchedStrategy.LEPT
1094 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.LEPT);
1095 case SchedStrategy.HOL
1096 % HOL (head-of-line, non-preemptive priority) maps to the
1097 % JAR's HOL, which routes to the exact M/G/1 priority
1098 % analyzer; collapsing it to FCFSPRIO would instead select
1099 % the egflin priority approximation and diverge from the
1100 % MATLAB-native result.
1101 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.HOL);
1102 case SchedStrategy.FCFSPRIO
1103 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.FCFSPRIO);
1104 case SchedStrategy.FORK
1105 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.FORK);
1106 case SchedStrategy.EXT
1107 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.EXT);
1108 case SchedStrategy.REF
1109 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.REF);
1110 case SchedStrategy.LCFSPR
1111 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.LCFSPR);
1112 case SchedStrategy.LCFSPI
1113 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.LCFSPI);
1114 case SchedStrategy.LCFSPRIO
1115 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.LCFSPRIO);
1116 case SchedStrategy.LCFSPRPRIO
1117 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.LCFSPRPRIO);
1118 case SchedStrategy.LCFSPIPRIO
1119 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.LCFSPIPRIO);
1120 case SchedStrategy.FCFSPR
1121 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.FCFSPR);
1122 case SchedStrategy.FCFSPI
1123 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.FCFSPI);
1124 case SchedStrategy.FCFSPRPRIO
1125 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.FCFSPRPRIO);
1126 case SchedStrategy.FCFSPIPRIO
1127 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.FCFSPIPRIO);
1128 case SchedStrategy.PSPRIO
1129 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.PSPRIO);
1130 case SchedStrategy.DPSPRIO
1131 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.DPSPRIO);
1132 case SchedStrategy.GPSPRIO
1133 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.GPSPRIO);
1134 case SchedStrategy.POLLING
1135 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.POLLING);
1136 case SchedStrategy.SRPT
1137 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.SRPT);
1138 case SchedStrategy.SRPTPRIO
1139 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.SRPTPRIO);
1140 case SchedStrategy.PSJF
1141 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.PSJF);
1142 case SchedStrategy.FB
1143 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.FB);
1144 case SchedStrategy.LRPT
1145 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.LRPT);
1146 case SchedStrategy.EDD
1147 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.EDD);
1148 case SchedStrategy.EDF
1149 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.EDF);
1150 case SchedStrategy.LPS
1151 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.LPS);
1152 case SchedStrategy.SETF
1153 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.SETF);
1154 case SchedStrategy.FSP
1155 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.FSP);
1156 case SchedStrategy.PAS
1157 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.PAS);
1158 case SchedStrategy.OI
1159 node_object = javaObject('jline.lang.nodes.Queue', jnetwork, line_node.getName, jline.lang.constant.SchedStrategy.OI);
1160 otherwise
1161 line_error(mfilename, sprintf('JLINE conversion does not support the %s scheduling strategy yet.', char(SchedStrategy.toText(line_node.schedStrategy))));
1162 end
1163 nservers = line_node.getNumberOfServers;
1164 if isinf(nservers)
1165 node_object.setNumberOfServers(java.lang.Integer.MAX_VALUE);
1166 elseif nservers > 1
1167 node_object.setNumberOfServers(line_node.getNumberOfServers);
1168 end
1169 if ~isempty(line_node.lldScaling)
1170 node_object.setLoadDependence(JLINE.from_line_matrix(line_node.lldScaling));
1171 end
1172 if ~isempty(line_node.lcdScaling)
1173 if isempty(sn)
1174 line_error(mfilename, "Class-dependent models require sn struct for MATLAB-to-JAVA translation.");
1175 end
1176 cdPeakJava = line_node.lcdScalingPeak;
1177 if isscalar(cdPeakJava)
1178 cdPeakJava = repmat(cdPeakJava, 1, sn.nclasses);
1179 end
1180 node_object.setLimitedClassDependence(JLINE.handle_to_serializablefun(line_node.lcdScaling, sn), JLINE.from_line_matrix(cdPeakJava(:)'));
1181 end
1182 % Set queue capacity if finite
1183 if ~isinf(line_node.cap)
1184 node_object.setCapacity(line_node.cap);
1185 end
1186 % Transfer LPS job limit (stored in schedStrategyPar(1))
1187 if line_node.schedStrategy == SchedStrategy.LPS && ~isempty(line_node.schedStrategyPar) && line_node.schedStrategyPar(1) >= 1
1188 node_object.setLimit(int32(line_node.schedStrategyPar(1)));
1189 end
1190 elseif isa(line_node, 'Source')
1191 node_object = javaObject('jline.lang.nodes.Source', jnetwork, line_node.getName);
1192 elseif isa(line_node, 'Sink')
1193 node_object = javaObject('jline.lang.nodes.Sink', jnetwork, line_node.getName);
1194 elseif isa(line_node, 'Router')
1195 node_object = javaObject('jline.lang.nodes.Router', jnetwork, line_node.getName);
1196 elseif isa(line_node, 'ClassSwitch')
1197 node_object = javaObject('jline.lang.nodes.ClassSwitch', jnetwork, line_node.getName);
1198 elseif isa(line_node, 'Fork')
1199 node_object = javaObject('jline.lang.nodes.Fork', jnetwork, line_node.name);
1200 node_object.setTasksPerLink(line_node.output.tasksPerLink);
1201 elseif isa(line_node, 'Join')
1202 node_object = javaObject('jline.lang.nodes.Join', jnetwork, line_node.name, forkNode);
1203 elseif isa(line_node, 'Logger')
1204 node_object = javaObject('jline.lang.nodes.Logger', jnetwork, line_node.name, [line_node.filePath,line_node.fileName]);
1205 % Transfer the output-field flags. MATLAB stores them as
1206 % 'true'/'false' char; the JAR booleans all default to false,
1207 % so transfer unconditionally to preserve MATLAB's defaults
1208 % (timestamp/jobID/jobClass enabled).
1209 node_object.setStartTime(strcmpi(char(line_node.getStartTime), 'true'));
1210 node_object.setLoggerName(strcmpi(char(line_node.getLoggerName), 'true'));
1211 node_object.setTimestamp(strcmpi(char(line_node.getTimestamp), 'true'));
1212 node_object.setJobID(strcmpi(char(line_node.getJobID), 'true'));
1213 node_object.setJobClass(strcmpi(char(line_node.getJobClass), 'true'));
1214 node_object.setTimeSameClass(strcmpi(char(line_node.getTimeSameClass), 'true'));
1215 node_object.setTimeAnyClass(strcmpi(char(line_node.getTimeAnyClass), 'true'));
1216 elseif isa(line_node, 'Cache')
1217 nitems = line_node.items.nitems;
1218 switch line_node.replacestrategy
1219 case ReplacementStrategy.RR
1220 repStrategy = jline.lang.constant.ReplacementStrategy.RR;
1221 case ReplacementStrategy.FIFO
1222 repStrategy = jline.lang.constant.ReplacementStrategy.FIFO;
1223 case ReplacementStrategy.SFIFO
1224 repStrategy = jline.lang.constant.ReplacementStrategy.SFIFO;
1225 case ReplacementStrategy.LRU
1226 repStrategy = jline.lang.constant.ReplacementStrategy.LRU;
1227 end
1228 if ~isempty(line_node.graph)
1229 % graph is a per-item cell array of (h+1)x(h+1) matrices
1230 gcells = line_node.graph;
1231 if ~iscell(gcells), gcells = {gcells}; end
1232 jGraph = javaArray('jline.util.matrix.Matrix', numel(gcells));
1233 for gidx = 1:numel(gcells)
1234 jGraph(gidx) = JLINE.from_line_matrix(gcells{gidx});
1235 end
1236 node_object = javaObject('jline.lang.nodes.Cache', jnetwork, line_node.name, nitems, JLINE.from_line_matrix(line_node.itemLevelCap), repStrategy, jGraph);
1237 else
1238 node_object = javaObject('jline.lang.nodes.Cache', jnetwork, line_node.name, nitems, JLINE.from_line_matrix(line_node.itemLevelCap), repStrategy);
1239 end
1240 elseif isa(line_node, 'Place')
1241 if line_node.isQueueing()
1242 % Queueing place: reconstruct with its scheduling strategy so
1243 % the JAR installs the matching embedded-queue server section
1244 % when setService is marshalled (see from_line_network).
1245 jsched = jline.lang.constant.SchedStrategy.fromText(SchedStrategy.toText(line_node.schedStrategy));
1246 node_object = javaObject('jline.lang.nodes.Place', jnetwork, line_node.getName, jsched);
1247 else
1248 node_object = javaObject('jline.lang.nodes.Place', jnetwork, line_node.getName);
1249 end
1250 elseif isa(line_node, 'Transition')
1251 node_object = javaObject('jline.lang.nodes.Transition', jnetwork, line_node.getName);
1252 % Modes are added later in from_line_network after classes are created
1253 else
1254 line_error(mfilename,'Node not supported by JLINE.');
1255 end
1256 end
1257
1258 function node_object = from_jline_node(jline_node, model, job_classes)
1259 if isa(jline_node, 'jline.lang.nodes.Delay')
1260 node_object = Delay(model, jline_node.getName.toCharArray');
1261 elseif isa(jline_node, 'jline.lang.nodes.Queue')
1262 schedStrategy = jline_node.getSchedStrategy;
1263 switch schedStrategy.name().toCharArray'
1264 case 'INF'
1265 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.INF);
1266 case 'FCFS'
1267 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.FCFS);
1268 case 'LCFS'
1269 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.LCFS);
1270 case 'SIRO'
1271 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.SIRO);
1272 case 'SJF'
1273 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.SJF);
1274 case 'LJF'
1275 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.LJF);
1276 case 'PS'
1277 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.PS);
1278 case 'DPS'
1279 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.DPS);
1280 case 'GPS'
1281 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.GPS);
1282 case 'SEPT'
1283 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.SEPT);
1284 case 'LEPT'
1285 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.LEPT);
1286 case {'HOL', 'FCFSPRIO'}
1287 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.FCFSPRIO);
1288 case 'FORK'
1289 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.FORK);
1290 case 'EXT'
1291 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.EXT);
1292 case 'REF'
1293 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.REF);
1294 case 'LCFSPR'
1295 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.LCFSPR);
1296 case 'SRPT'
1297 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.SRPT);
1298 case 'SRPTPRIO'
1299 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.SRPTPRIO);
1300 case 'PSJF'
1301 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.PSJF);
1302 case 'FB'
1303 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.FB);
1304 case 'LRPT'
1305 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.LRPT);
1306 case 'PSPRIO'
1307 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.PSPRIO);
1308 case 'DPSPRIO'
1309 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.DPSPRIO);
1310 case 'GPSPRIO'
1311 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.GPSPRIO);
1312 case 'LCFSPI'
1313 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.LCFSPI);
1314 case 'LCFSPRIO'
1315 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.LCFSPRIO);
1316 case 'LCFSPRPRIO'
1317 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.LCFSPRPRIO);
1318 case 'LCFSPIPRIO'
1319 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.LCFSPIPRIO);
1320 case 'FCFSPR'
1321 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.FCFSPR);
1322 case 'FCFSPI'
1323 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.FCFSPI);
1324 case 'FCFSPRPRIO'
1325 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.FCFSPRPRIO);
1326 case 'FCFSPIPRIO'
1327 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.FCFSPIPRIO);
1328 case 'POLLING'
1329 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.POLLING);
1330 case 'EDD'
1331 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.EDD);
1332 case 'EDF'
1333 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.EDF);
1334 case 'LPS'
1335 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.LPS);
1336 case 'SETF'
1337 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.SETF);
1338 case 'FSP'
1339 node_object = Queue(model, jline_node.getName.toCharArray', SchedStrategy.FSP);
1340 case 'PAS'
1341 % The PAS service rate function mu(c) lives in a Java
1342 % SerializableFunction and cannot be reconstructed as a
1343 % MATLAB handle
1344 line_error(mfilename, 'JLINE-to-LINE conversion does not support PAS queues (service rate function not recoverable).');
1345 otherwise
1346 line_error(mfilename, sprintf('JLINE-to-LINE conversion does not support the %s scheduling strategy yet.', char(schedStrategy.name())));
1347 end
1348 node_object.setNumberOfServers(jline_node.getNumberOfServers);
1349 cap = jline_node.getCap();
1350 if cap < intmax && cap > 0
1351 node_object.setCapacity(cap);
1352 end
1353 if ~isempty(JLINE.from_jline_matrix(jline_node.getLimitedLoadDependence))
1354 node_object.setLoadDependence(JLINE.from_jline_matrix(jline_node.getLimitedLoadDependence));
1355 end
1356 elseif isa(jline_node, 'jline.lang.nodes.Source')
1357 node_object = Source(model, jline_node.getName.toCharArray');
1358 elseif isa(jline_node, 'jline.lang.nodes.Sink')
1359 node_object = Sink(model, jline_node.getName.toCharArray');
1360 elseif isa(jline_node, 'jline.lang.nodes.Router')
1361 node_object = Router(model, jline_node.getName.toCharArray');
1362 elseif isa(jline_node, 'jline.lang.nodes.ClassSwitch')
1363 nClasses = job_classes.size;
1364 csMatrix = zeros(nClasses, nClasses);
1365 for r = 1:nClasses
1366 for s = 1:nClasses
1367 csMatrix(r,s) = jline_node.getServer.applyCsFun(r-1,s-1);
1368 end
1369 end
1370 node_object = ClassSwitch(model, jline_node.getName.toCharArray', csMatrix);
1371 elseif isa(jline_node, 'jline.lang.nodes.Cache')
1372 numItems = jline_node.getNumberOfItems();
1373 itemLevelCap = JLINE.from_jline_matrix(jline_node.getItemLevelCap());
1374 replPolicy = jline_node.getReplacementStrategy();
1375 switch char(replPolicy)
1376 case 'LRU'
1377 rp = ReplacementStrategy.LRU;
1378 case 'FIFO'
1379 rp = ReplacementStrategy.FIFO;
1380 case 'RR'
1381 rp = ReplacementStrategy.RR;
1382 otherwise
1383 rp = ReplacementStrategy.LRU;
1384 end
1385 node_object = Cache(model, jline_node.getName.toCharArray', numItems, itemLevelCap, rp);
1386 % hitClass, missClass, and popularity set later in jline_to_line
1387 elseif isa(jline_node, 'jline.lang.nodes.Fork')
1388 node_object = Fork(model, jline_node.getName.toCharArray');
1389 tpl = jline_node.getOutput().tasksPerLink;
1390 if tpl > 1
1391 node_object.setTasksPerLink(tpl);
1392 end
1393 elseif isa(jline_node, 'jline.lang.nodes.Join')
1394 node_object = Join(model, jline_node.getName.toCharArray');
1395 % joinOf is set later in jline_to_line after all nodes are created
1396 elseif isa(jline_node, 'jline.lang.nodes.Place')
1397 node_object = Place(model, jline_node.getName.toCharArray');
1398 elseif isa(jline_node, 'jline.lang.nodes.Transition')
1399 node_object = Transition(model, jline_node.getName.toCharArray');
1400 % Note: Mode configurations need to be set after classes are created
1401 else
1402 line_error(mfilename,'Node not supported by JLINE.');
1403 end
1404 end
1405
1406 function node_class = from_line_class(line_class, jnetwork)
1407 % Check signal classes first (before their base classes)
1408 if isa(line_class, 'ClosedSignal')
1409 % ClosedSignal -> jline.lang.ClosedSignal
1410 jSignalType = jline.lang.constant.SignalType.fromID(line_class.signalType);
1411 [jRemDist, jRemPol] = JLINE.from_line_signal_removal(line_class);
1412 if isempty(jRemDist) && isempty(jRemPol)
1413 node_class = javaObject('jline.lang.ClosedSignal', jnetwork, line_class.getName, jSignalType, jnetwork.getNodeByName(line_class.refstat.getName), line_class.priority);
1414 else
1415 node_class = javaObject('jline.lang.ClosedSignal', jnetwork, line_class.getName, jSignalType, jnetwork.getNodeByName(line_class.refstat.getName), line_class.priority, jRemDist, jRemPol);
1416 end
1417 elseif isa(line_class, 'Signal') || isa(line_class, 'OpenSignal')
1418 % Signal/OpenSignal -> jline.lang.Signal (includes CATASTROPHE type)
1419 jSignalType = jline.lang.constant.SignalType.fromID(line_class.signalType);
1420 [jRemDist, jRemPol] = JLINE.from_line_signal_removal(line_class);
1421 if isempty(jRemDist) && isempty(jRemPol)
1422 node_class = javaObject('jline.lang.Signal', jnetwork, line_class.getName, jSignalType, line_class.priority);
1423 else
1424 node_class = javaObject('jline.lang.Signal', jnetwork, line_class.getName, jSignalType, line_class.priority, jRemDist, jRemPol);
1425 end
1426 elseif isa(line_class, 'OpenClass')
1427 node_class = javaObject('jline.lang.OpenClass', jnetwork, line_class.getName, line_class.priority);
1428 elseif isa(line_class, 'SelfLoopingClass')
1429 node_class = javaObject('jline.lang.SelfLoopingClass', jnetwork, line_class.getName, line_class.population, jnetwork.getNodeByName(line_class.refstat.getName), line_class.priority);
1430 elseif isa(line_class, 'ClosedClass')
1431 node_class = javaObject('jline.lang.ClosedClass', jnetwork, line_class.getName, line_class.population, jnetwork.getNodeByName(line_class.refstat.getName), line_class.priority);
1432 else
1433 line_error(mfilename,'Class type not supported by JLINE.');
1434 end
1435 % Transfer relative deadline (used by EDD/EDF scheduling)
1436 if isprop(line_class, 'deadline') && ~isempty(line_class.deadline) && ~isinf(line_class.deadline)
1437 node_class.setDeadline(line_class.deadline);
1438 end
1439 if line_class.isReferenceClass()
1440 node_class.setReferenceClass(true);
1441 end
1442 end
1443
1444 function node_class = from_jline_class(jclass, model)
1445 % Check signal classes first (their base classes would match below)
1446 if isa(jclass, 'jline.lang.ClosedSignal')
1447 [remDist, remPol] = JLINE.from_jline_signal_removal(jclass);
1448 node_class = ClosedSignal(model, jclass.getName.toCharArray', jclass.getSignalType().getID(), model.getNodeByName(jclass.getReferenceStation.getName), jclass.getPriority, remDist, remPol);
1449 elseif isa(jclass, 'jline.lang.OpenSignal')
1450 node_class = OpenSignal(model, jclass.getName.toCharArray', jclass.getSignalType().getID(), jclass.getPriority);
1451 elseif isa(jclass, 'jline.lang.Signal')
1452 [remDist, remPol] = JLINE.from_jline_signal_removal(jclass);
1453 node_class = Signal(model, jclass.getName.toCharArray', jclass.getSignalType().getID(), jclass.getPriority, remDist, remPol);
1454 elseif isa(jclass, 'jline.lang.OpenClass')
1455 node_class = OpenClass(model, jclass.getName.toCharArray', jclass.getPriority);
1456 elseif isa(jclass, 'jline.lang.SelfLoopingClass')
1457 node_class = SelfLoopingClass(model, jclass.getName.toCharArray', jclass.getNumberOfJobs, model.getNodeByName(jclass.getReferenceStation.getName), jclass.getPriority);
1458 elseif isa(jclass, 'jline.lang.ClosedClass')
1459 node_class = ClosedClass(model, jclass.getName.toCharArray', jclass.getNumberOfJobs, model.getNodeByName(jclass.getReferenceStation.getName), jclass.getPriority);
1460 else
1461 line_error(mfilename,'Class type not supported by JLINE.');
1462 end
1463 % Transfer relative deadline (used by EDD/EDF scheduling)
1464 dl = jclass.getDeadline();
1465 if isfinite(dl)
1466 node_class.deadline = dl;
1467 end
1468 end
1469
1470 function [remDist, remPol] = from_jline_signal_removal(jclass)
1471 % FROM_JLINE_SIGNAL_REMOVAL Read back a JAR signal's batch-removal
1472 % distribution and policy for the MATLAB signal constructors.
1473 jRemDist = jclass.getRemovalDistribution();
1474 if isempty(jRemDist)
1475 remDist = [];
1476 else
1477 remDist = JLINE.from_jline_distribution(jRemDist);
1478 end
1479 jRemPol = jclass.getRemovalPolicy();
1480 if isempty(jRemPol)
1481 remPol = RemovalPolicy.RANDOM;
1482 else
1483 remPol = jRemPol.getID();
1484 end
1485 end
1486
1487 function from_line_links(model, jmodel)
1488 connections = model.getConnectionMatrix();
1489 [m, ~] = size(connections);
1490 jnodes = jmodel.getNodes();
1491 jclasses = jmodel.getClasses();
1492 njclasses = jclasses.size();
1493 line_nodes = model.getNodes;
1494 sn = model.getStruct;
1495
1496 % Build mapping from MATLAB node index to Java node index
1497 % (accounting for skipped auto-added ClassSwitch nodes)
1498 matlab2java_node_idx = zeros(1, length(line_nodes));
1499 jidx = 0;
1500 for i = 1:length(line_nodes)
1501 if isa(line_nodes{i}, 'ClassSwitch') && line_nodes{i}.autoAdded
1502 matlab2java_node_idx(i) = -1; % Mark as skipped
1503 else
1504 matlab2java_node_idx(i) = jidx;
1505 jidx = jidx + 1;
1506 end
1507 end
1508
1509 %nodevisits = cellsum(sn.nodevisits);
1510 % [ ] Update to consider different weights/routing for classes
1511 if isempty(sn.rtorig)
1512 useLinkMethod = false; % this model did not call link()
1513 else
1514 jrt_matrix = jmodel.initRoutingMatrix();
1515 useLinkMethod = true;
1516 end
1517
1518 % For models with auto-added ClassSwitch nodes, use sn.rtorig directly
1519 % to set up routing with proper class switching
1520 hasAutoCS = false;
1521 for i = 1:length(line_nodes)
1522 if isa(line_nodes{i}, 'ClassSwitch') && line_nodes{i}.autoAdded
1523 hasAutoCS = true;
1524 break;
1525 end
1526 end
1527
1528 if useLinkMethod && hasAutoCS
1529 % Use sn.rtorig directly - it contains the full routing with class switching
1530 % sn.rtorig already excludes auto-added ClassSwitch nodes (it's based on nstations)
1531 % So we iterate over the rtorig matrix dimensions directly
1532 for r = 1:njclasses
1533 for s = 1:njclasses
1534 if ~isempty(sn.rtorig{r,s})
1535 Prs = sn.rtorig{r,s};
1536 [nrows, ncols] = size(Prs);
1537 for i = 1:nrows
1538 for j = 1:ncols
1539 if Prs(i,j) > 0
1540 % sn.rtorig uses station indices which map to non-CS nodes
1541 % Find the java node indices by matching station index to node
1542 jsrc_idx = i - 1; % Direct mapping since rtorig excludes CS
1543 jdest_idx = j - 1;
1544 jrt_matrix.set(jclasses.get(r-1), jclasses.get(s-1), jnodes.get(jsrc_idx), jnodes.get(jdest_idx), Prs(i,j));
1545 end
1546 end
1547 end
1548 end
1549 end
1550 end
1551 else
1552 % Original logic for models without auto-added ClassSwitch
1553 for i = 1:m
1554 line_node = line_nodes{i};
1555
1556 % Skip auto-added ClassSwitch nodes - Java will add them automatically
1557 if isa(line_node, 'ClassSwitch') && line_node.autoAdded
1558 continue;
1559 end
1560
1561 jnode_idx = matlab2java_node_idx(i);
1562 for k = 1:njclasses
1563 output_strat = line_node.output.outputStrategy{k};
1564 switch RoutingStrategy.fromText(output_strat{2})
1565 case RoutingStrategy.DISABLED
1566 jnodes.get(jnode_idx).setRouting(jclasses.get(k-1),jline.lang.constant.RoutingStrategy.DISABLED);
1567 case RoutingStrategy.RAND
1568 jnodes.get(jnode_idx).setRouting(jclasses.get(k-1),jline.lang.constant.RoutingStrategy.RAND);
1569 outlinks_i=find(connections(i,:));
1570 if useLinkMethod
1571 % Do NOT add routing matrix entries for RAND routing.
1572 % JAR's getRoutingMatrix computes RAND routing from
1573 % the connection matrix (matching MATLAB behavior).
1574 % Adding entries would cause link() to convert RAND
1575 % to PROB, and for closed classes would incorrectly
1576 % include Sink connections.
1577 else
1578 for j= outlinks_i(:)'
1579 jdest_idx = matlab2java_node_idx(j);
1580 if jdest_idx >= 0
1581 jmodel.addLink(jnodes.get(jnode_idx), jnodes.get(jdest_idx));
1582 end
1583 end
1584 end
1585 case RoutingStrategy.RROBIN
1586 jnodes.get(jnode_idx).setRouting(jclasses.get(k-1),jline.lang.constant.RoutingStrategy.RROBIN);
1587 outlinks_i=find(connections(i,:))';
1588 if useLinkMethod
1589 line_error(mfilename,'RROBIN cannot be used together with the link() command.');
1590 end
1591 for j= outlinks_i(:)'
1592 jdest_idx = matlab2java_node_idx(j);
1593 if jdest_idx >= 0
1594 jmodel.addLink(jnodes.get(jnode_idx), jnodes.get(jdest_idx));
1595 end
1596 end
1597 case RoutingStrategy.WRROBIN
1598 outlinks_i=find(connections(i,:))';
1599 for j= outlinks_i(:)'
1600 jdest_idx = matlab2java_node_idx(j);
1601 if jdest_idx >= 0
1602 jmodel.addLink(jnodes.get(jnode_idx), jnodes.get(jdest_idx));
1603 end
1604 end
1605 if useLinkMethod
1606 line_error(mfilename,'RROBIN cannot be used together with the link() command.');
1607 end
1608 for j= 1:length(output_strat{3})
1609 node_target = jmodel.getNodeByName(output_strat{3}{j}{1}.getName());
1610 weight = output_strat{3}{j}{2};
1611 jnodes.get(jnode_idx).setRouting(jclasses.get(k-1),jline.lang.constant.RoutingStrategy.WRROBIN, node_target, weight);
1612 end
1613 case RoutingStrategy.PROB
1614 outlinks_i=find(connections(i,:));
1615 jnodes.get(jnode_idx).setRouting(jclasses.get(k-1), jline.lang.constant.RoutingStrategy.PROB);
1616 if ~useLinkMethod
1617 for j= outlinks_i(:)'
1618 jdest_idx = matlab2java_node_idx(j);
1619 if jdest_idx >= 0
1620 jmodel.addLink(jnodes.get(jnode_idx), jnodes.get(jdest_idx));
1621 end
1622 end
1623 end
1624 if length(output_strat) >= 3
1625 probabilities = output_strat{3};
1626 for j = 1:length(probabilities)
1627 dest_idx = probabilities{j}{1}.index;
1628 jdest_idx = matlab2java_node_idx(dest_idx);
1629 if (connections(i, dest_idx) ~= 0) && jdest_idx >= 0
1630 if useLinkMethod
1631 jrt_matrix.set(jclasses.get(k-1), jclasses.get(k-1), jnodes.get(jnode_idx), jnodes.get(jdest_idx), probabilities{j}{2});
1632 else
1633 jnodes.get(jnode_idx).setProbRouting(jclasses.get(k-1), jnodes.get(jdest_idx), probabilities{j}{2});
1634 end
1635 end
1636 end
1637 end
1638 case RoutingStrategy.JSQ
1639 jnodes.get(jnode_idx).setRouting(jclasses.get(k-1),jline.lang.constant.RoutingStrategy.JSQ);
1640 outlinks_i=find(connections(i,:))';
1641 if ~useLinkMethod
1642 for j= outlinks_i(:)'
1643 jdest_idx = matlab2java_node_idx(j);
1644 if jdest_idx >= 0
1645 jmodel.addLink(jnodes.get(jnode_idx), jnodes.get(jdest_idx));
1646 end
1647 end
1648 end
1649 case RoutingStrategy.KCHOICES
1650 jnodes.get(jnode_idx).setRouting(jclasses.get(k-1),jline.lang.constant.RoutingStrategy.KCHOICES);
1651 if length(output_strat) >= 3 && length(output_strat{3}) >= 2
1652 kparam = output_strat{3}{1};
1653 memparam = logical(output_strat{3}{2});
1654 jnodes.get(jnode_idx).setKChoicesRouting(jclasses.get(k-1), int32(kparam), memparam);
1655 end
1656 outlinks_i=find(connections(i,:))';
1657 if ~useLinkMethod
1658 for j= outlinks_i(:)'
1659 jdest_idx = matlab2java_node_idx(j);
1660 if jdest_idx >= 0
1661 jmodel.addLink(jnodes.get(jnode_idx), jnodes.get(jdest_idx));
1662 end
1663 end
1664 end
1665 case RoutingStrategy.RL
1666 jnodes.get(jnode_idx).setRouting(jclasses.get(k-1),jline.lang.constant.RoutingStrategy.RL);
1667 outlinks_i=find(connections(i,:))';
1668 if ~useLinkMethod
1669 for j= outlinks_i(:)'
1670 jdest_idx = matlab2java_node_idx(j);
1671 if jdest_idx >= 0
1672 jmodel.addLink(jnodes.get(jnode_idx), jnodes.get(jdest_idx));
1673 end
1674 end
1675 end
1676 % Forward the value function, action nodes, and
1677 % state size from outputStrategy{r}{3..5} so the
1678 % JAR sub_rl can replicate MATLAB's decisions.
1679 if length(output_strat) >= 5
1680 valFnRaw = output_strat{3};
1681 nodesNeedAction = output_strat{4};
1682 stateSize = output_strat{5};
1683 if ~isempty(valFnRaw)
1684 if stateSize == 0
1685 % Tabular: flatten N-D array to a row vector and pass shape.
1686 shp = size(valFnRaw);
1687 jvfFlat = jline.util.matrix.Matrix(1, numel(valFnRaw));
1688 flat = valFnRaw(:);
1689 for f = 1:numel(flat)
1690 jvfFlat.set(0, f-1, double(flat(f)));
1691 end
1692 jshape = int32(shp(:)');
1693 jnna = int32(nodesNeedAction(:)' - 1); % MATLAB->Java 0-based
1694 jnodes.get(jnode_idx).setRLRouting(jclasses.get(k-1), jvfFlat, jshape, jnna, int32(stateSize));
1695 else
1696 % Linear approx: coefficient row vector.
1697 coeff = valFnRaw(:)';
1698 jvf = jline.util.matrix.Matrix(1, numel(coeff));
1699 for f = 1:numel(coeff)
1700 jvf.set(0, f-1, double(coeff(f)));
1701 end
1702 jnna = int32(nodesNeedAction(:)' - 1);
1703 jnodes.get(jnode_idx).setRLRouting(jclasses.get(k-1), jvf, int32([]), jnna, int32(stateSize));
1704 end
1705 end
1706 end
1707 otherwise
1708 line_warning(mfilename, sprintf('''%s'' routing strategy not supported by JLINE, setting as Disabled.\n',output_strat{2}));
1709 jnodes.get(jnode_idx).setRouting(jclasses.get(k-1),jline.lang.constant.RoutingStrategy.DISABLED);
1710 end
1711 end
1712 end
1713 end
1714 if useLinkMethod
1715 jmodel.link(jrt_matrix);
1716 % Align the sn.rtorig be the same, treating artificial
1717 % ClassSwitch nodes as if they were explicitly specified
1718 jsn = jmodel.getStruct(true);
1719 rtorig = java.util.HashMap();
1720 if ~isempty(model.sn.rtorig)
1721 if iscell(model.sn.rtorig)
1722 for r = 1:njclasses
1723 sub_rtorig = java.util.HashMap();
1724 for s = 1:njclasses
1725 sub_rtorig.put(jclasses.get(s-1), JLINE.from_line_matrix(model.sn.rtorig{r,s}));
1726 end
1727 rtorig.put(jclasses.get(r-1), sub_rtorig);
1728 end
1729 end
1730 end
1731 jsn.rtorig = rtorig;
1732 end
1733 end
1734
1735 function model = from_jline_routing(model, jnetwork)
1736 jnodes = jnetwork.getNodes();
1737 jclasses = jnetwork.getClasses();
1738 n_nodes = jnodes.size();
1739 network_nodes = model.getNodes;
1740 network_classes = model.getClasses;
1741
1742 % Build name-to-MATLAB-node map (JAR and MATLAB may order nodes differently)
1743 node_by_name = containers.Map();
1744 for nn = 1:length(network_nodes)
1745 node_by_name(network_nodes{nn}.name) = network_nodes{nn};
1746 end
1747
1748 connections = JLINE.from_jline_matrix(jnetwork.getConnectionMatrix());
1749 [row,col] = find(connections);
1750 for i=1:length(row)
1751 from_name = char(jnodes.get(row(i)-1).getName());
1752 to_name = char(jnodes.get(col(i)-1).getName());
1753 model.addLink(node_by_name(from_name), node_by_name(to_name));
1754 end
1755
1756 for n = 1 : n_nodes
1757 jnode = jnodes.get(n-1);
1758 cur_node = node_by_name(char(jnode.getName()));
1759 output_strategies = jnode.getOutputStrategies();
1760 n_strategies = output_strategies.size();
1761 for m = 1 : n_strategies
1762 output_strat = output_strategies.get(m-1);
1763 routing_strat = output_strat.getRoutingStrategy;
1764 routing_strat_classidx = output_strat.getJobClass.getIndex();
1765 switch char(routing_strat)
1766 case 'RAND'
1767 cur_node.setRouting(network_classes{routing_strat_classidx}, RoutingStrategy.RAND);
1768 case 'RROBIN'
1769 cur_node.setRouting(network_classes{routing_strat_classidx}, RoutingStrategy.RROBIN);
1770 case 'WRROBIN'
1771 dest = output_strat.getDestination();
1772 if ~isempty(dest)
1773 dest_name = char(dest.getName());
1774 if node_by_name.isKey(dest_name)
1775 weight = output_strat.getProbability();
1776 cur_node.setRouting(network_classes{routing_strat_classidx}, RoutingStrategy.WRROBIN, node_by_name(dest_name), weight);
1777 end
1778 end
1779 case 'DISABLED'
1780 cur_node.setRouting(network_classes{routing_strat_classidx}, RoutingStrategy.DISABLED);
1781 end
1782 end
1783 end
1784 end
1785
1786 function model = from_jline_links(model, jnetwork)
1787 P = model.initRoutingMatrix;
1788 jnodes = jnetwork.getNodes();
1789 jclasses = jnetwork.getClasses();
1790 n_classes = jclasses.size();
1791 n_nodes = jnodes.size();
1792 network_nodes = model.getNodes;
1793
1794 % Build JAR-to-MATLAB node index mapping (node orders may differ)
1795 jar2ml = zeros(1, n_nodes);
1796 for jj = 1:n_nodes
1797 jar_name = char(jnodes.get(jj-1).getName());
1798 for mm = 1:length(network_nodes)
1799 if strcmp(network_nodes{mm}.name, jar_name)
1800 jar2ml(jj) = mm;
1801 break;
1802 end
1803 end
1804 end
1805
1806 hasDestinations = false;
1807 for n = 1 : n_nodes
1808 jnode = jnodes.get(n-1);
1809 output_strategies = jnode.getOutputStrategies();
1810 n_strategies = output_strategies.size();
1811 for m = 1 : n_strategies
1812 output_strat = output_strategies.get(m-1);
1813 dest = output_strat.getDestination();
1814 if~isempty(dest) % disabled strategy
1815 hasDestinations = true;
1816 in_idx = jar2ml(jnetwork.getNodeIndex(jnode)+1);
1817 out_idx = jar2ml(jnetwork.getNodeIndex(dest)+1);
1818 if n_classes == 1
1819 P{1}(in_idx,out_idx) = output_strat.getProbability();
1820 else
1821 strat_class = output_strat.getJobClass();
1822 class_idx = jnetwork.getJobClassIndex(strat_class)+1;
1823 P{class_idx,class_idx}(in_idx,out_idx) = output_strat.getProbability();
1824 end
1825 end
1826 end
1827 end
1828
1829 % If no OutputStrategy entries had destinations (e.g., model
1830 % loaded from JSON via LineModelIO.load), fall back to rtorig
1831 if ~hasDestinations
1832 sn = jnetwork.getStruct;
1833 if ~isempty(sn.rtorig)
1834 % rtorig is station-indexed (no permutation needed —
1835 % station ordering matches between JAR and MATLAB)
1836 for r = 1:n_classes
1837 for s = 1:n_classes
1838 rtMat = JLINE.from_jline_matrix(sn.rtorig.get(jclasses.get(r-1)).get(jclasses.get(s-1)));
1839 if ~isempty(rtMat)
1840 P{r,s} = rtMat;
1841 end
1842 end
1843 end
1844 end
1845 end
1846
1847 model.link(P);
1848
1849 % Restore non-PROB routing strategies (RROBIN, WRROBIN, etc.)
1850 % after link(), which sets all routing to PROB
1851 network_nodes = model.getNodes;
1852 network_classes = model.getClasses;
1853 node_by_name = containers.Map();
1854 for nn = 1:length(network_nodes)
1855 node_by_name(network_nodes{nn}.name) = network_nodes{nn};
1856 end
1857 for n = 1 : n_nodes
1858 jnode = jnodes.get(n-1);
1859 cur_node = node_by_name(char(jnode.getName()));
1860 output_strategies = jnode.getOutputStrategies();
1861 n_strategies = output_strategies.size();
1862 for m = 1 : n_strategies
1863 output_strat = output_strategies.get(m-1);
1864 routing_strat = output_strat.getRoutingStrategy;
1865 routing_strat_classidx = output_strat.getJobClass.getIndex();
1866 switch char(routing_strat)
1867 case 'RROBIN'
1868 cur_node.setRouting(network_classes{routing_strat_classidx}, RoutingStrategy.RROBIN);
1869 case 'WRROBIN'
1870 dest = output_strat.getDestination();
1871 if ~isempty(dest)
1872 dest_name = char(dest.getName());
1873 if node_by_name.isKey(dest_name)
1874 % Clear stale PROB entries before first WRROBIN weight
1875 classIdx = routing_strat_classidx;
1876 if length(cur_node.output.outputStrategy) >= classIdx && ...
1877 length(cur_node.output.outputStrategy{1, classIdx}) >= 3
1878 curStrat = cur_node.output.outputStrategy{1, classIdx}{2};
1879 if ~strcmp(curStrat, 'WeightedRoundRobin')
1880 cur_node.output.outputStrategy{1, classIdx}{3} = {};
1881 end
1882 end
1883 weight = output_strat.getProbability();
1884 cur_node.setRouting(network_classes{routing_strat_classidx}, RoutingStrategy.WRROBIN, node_by_name(dest_name), weight);
1885 end
1886 end
1887 end
1888 end
1889 end
1890
1891 % Invalidate cached struct after modifying routing strategies
1892 model.resetStruct();
1893
1894 %Align the sn.rtorig be the same (Assume Java network is
1895 %created by calling Network.link)
1896 sn = jnetwork.getStruct;
1897 rtorig = cell(n_classes, n_classes);
1898 for r = 1:n_classes
1899 for s = 1:n_classes
1900 rtorig{r,s} = JLINE.from_jline_matrix(sn.rtorig.get(jclasses.get(r-1)).get(jclasses.get(s-1)));
1901 end
1902 end
1903 model.sn.rtorig = rtorig;
1904 end
1905
1906 function [jnetwork] = from_line_network(model)
1907 %w = warning;
1908 %warning('off');
1909 sn = model.getStruct;
1910
1911 jnetwork = javaObject('jline.lang.Network', model.getName);
1912 line_nodes = model.getNodes;
1913 line_classes = model.getClasses;
1914
1915 jnodes = cell(1,length(line_nodes));
1916 jclasses = cell(1,length(line_classes));
1917
1918 for n = 1 : length(line_nodes)
1919 % Skip auto-added ClassSwitch nodes - Java's link() will add them automatically
1920 if isa(line_nodes{n}, 'ClassSwitch') && line_nodes{n}.autoAdded
1921 continue;
1922 end
1923 if isa(line_nodes{n}, 'Join')
1924 jnodes{n} = JLINE.from_line_node(line_nodes{n}, jnetwork, line_classes, jnodes{line_nodes{n}.joinOf.index}, sn);
1925 else
1926 jnodes{n} = JLINE.from_line_node(line_nodes{n}, jnetwork, line_classes, [], sn);
1927 end
1928 end
1929
1930 for n = 1 : length(line_classes)
1931 jclasses{n} = JLINE.from_line_class(line_classes{n}, jnetwork);
1932 end
1933
1934 % Set up forJobClass associations for signal classes
1935 for n = 1 : length(line_classes)
1936 if (isa(line_classes{n}, 'Signal') || isa(line_classes{n}, 'OpenSignal') || isa(line_classes{n}, 'ClosedSignal'))
1937 if ~isempty(line_classes{n}.targetJobClass)
1938 targetIdx = line_classes{n}.targetJobClass.index;
1939 jclasses{n}.forJobClass(jclasses{targetIdx});
1940 end
1941 end
1942 end
1943
1944 for n = 1: length(jnodes)
1945 if isempty(jnodes{n})
1946 continue; % Skip nodes that were not converted (e.g., auto-added ClassSwitch)
1947 end
1948 JLINE.set_service(line_nodes{n}, jnodes{n}, line_classes);
1949 JLINE.set_delayoff(line_nodes{n}, jnodes{n}, line_classes);
1950 end
1951
1952 % Set drop rules for stations (after classes are created)
1953 for n = 1: length(jnodes)
1954 if isempty(jnodes{n})
1955 continue; % Skip nodes that were not converted
1956 end
1957 if isa(line_nodes{n}, 'Station') && ~isa(line_nodes{n}, 'Source')
1958 for r = 1:length(line_classes)
1959 if length(line_nodes{n}.dropRule) >= r && ~isempty(line_nodes{n}.dropRule(r))
1960 dropRule = line_nodes{n}.dropRule(r);
1961 switch dropRule
1962 case DropStrategy.DROP
1963 jnodes{n}.setDropRule(jclasses{r}, jline.lang.constant.DropStrategy.Drop);
1964 case DropStrategy.BAS
1965 jnodes{n}.setDropRule(jclasses{r}, jline.lang.constant.DropStrategy.BlockingAfterService);
1966 case DropStrategy.BBS
1967 jnodes{n}.setDropRule(jclasses{r}, jline.lang.constant.DropStrategy.BlockingBeforeService);
1968 case DropStrategy.RSRD
1969 jnodes{n}.setDropRule(jclasses{r}, jline.lang.constant.DropStrategy.ReServiceOnRejection);
1970 % WAITQ (-1) is MATLAB's universal default drop rule
1971 % for every station-class pair, not a user request.
1972 % The JAR treats an absent (null) drop rule as the
1973 % same waiting-queue default, and its refreshCapacity
1974 % guard rejects an EXPLICIT setDropRule(WAITQ) at a
1975 % finite buffer for an open class. Marshalling the
1976 % default WAITQ explicitly would fabricate that user
1977 % intent and make the guard misfire on the derived
1978 % classCap=0 that an unserved open class carries
1979 % (e.g. InitClass at Delay1, Class1 at Queue1). So do
1980 % not transfer WAITQ: only the genuine blocking/loss
1981 % policies above carry user intent.
1982 end
1983 end
1984 end
1985 % Transfer per-class capacity limits (setChainCapacity/classCap)
1986 if ~isempty(line_nodes{n}.classCap)
1987 for r = 1:min(length(line_classes), length(line_nodes{n}.classCap))
1988 if isfinite(line_nodes{n}.classCap(r)) && line_nodes{n}.classCap(r) >= 0
1989 jnodes{n}.setClassCap(jclasses{r}, int32(line_nodes{n}.classCap(r)));
1990 end
1991 end
1992 end
1993 end
1994 end
1995
1996 % Set polling type and switchover times for polling queues
1997 for n = 1: length(jnodes)
1998 if isempty(jnodes{n})
1999 continue;
2000 end
2001 if isa(line_nodes{n}, 'Queue') && line_nodes{n}.schedStrategy == SchedStrategy.POLLING
2002 % Set polling type
2003 if ~isempty(line_nodes{n}.pollingType) && ~isempty(line_nodes{n}.pollingType{1})
2004 pollingType = line_nodes{n}.pollingType{1};
2005 switch pollingType
2006 case PollingType.GATED
2007 jPollingType = jline.lang.constant.PollingType.GATED;
2008 case PollingType.EXHAUSTIVE
2009 jPollingType = jline.lang.constant.PollingType.EXHAUSTIVE;
2010 case PollingType.KLIMITED
2011 jPollingType = jline.lang.constant.PollingType.KLIMITED;
2012 case PollingType.DECREMENTING
2013 jPollingType = jline.lang.constant.PollingType.DECREMENTING;
2014 otherwise
2015 line_error(mfilename, sprintf('Unsupported polling type for the Java backend: %d.', PollingType.toId(pollingType)));
2016 end
2017 if pollingType == PollingType.KLIMITED && ~isempty(line_nodes{n}.pollingPar)
2018 jnodes{n}.setPollingType(jPollingType, int32(line_nodes{n}.pollingPar));
2019 else
2020 jnodes{n}.setPollingType(jPollingType);
2021 end
2022 end
2023 % Set switchover times
2024 if ~isempty(line_nodes{n}.switchoverTime)
2025 for r = 1:length(line_classes)
2026 if length(line_nodes{n}.switchoverTime) >= r && ~isempty(line_nodes{n}.switchoverTime{r})
2027 soTime = line_nodes{n}.switchoverTime{r};
2028 if ~isa(soTime, 'Immediate')
2029 jnodes{n}.setSwitchover(jclasses{r}, JLINE.from_line_distribution(soTime));
2030 end
2031 end
2032 end
2033 end
2034 end
2035 end
2036
2037 % Transfer impatience features (reneging, balking, retrial) for queues
2038 % These require classes to be created first.
2039 for n = 1: length(jnodes)
2040 if isempty(jnodes{n})
2041 continue;
2042 end
2043 if ~isa(line_nodes{n}, 'Queue')
2044 continue;
2045 end
2046 % Immediate feedback (self-looping jobs stay in service): either
2047 % 'all' or a cell array of class indices on the MATLAB side
2048 if ~isempty(line_nodes{n}.immediateFeedback)
2049 if ischar(line_nodes{n}.immediateFeedback)
2050 jnodes{n}.setImmediateFeedback(true);
2051 elseif iscell(line_nodes{n}.immediateFeedback)
2052 for fbc = 1:length(line_nodes{n}.immediateFeedback)
2053 jnodes{n}.setImmediateFeedback(jclasses{line_nodes{n}.immediateFeedback{fbc}});
2054 end
2055 end
2056 end
2057 for r = 1:length(line_classes)
2058 % Reneging (timer-based patience). Only RENEGING is supported;
2059 % BALKING via setPatience is rejected by both MATLAB and the JAR.
2060 if ~isempty(line_nodes{n}.patienceDistributions) && ...
2061 r <= length(line_nodes{n}.patienceDistributions) && ...
2062 ~isempty(line_nodes{n}.patienceDistributions{1, r})
2063 patDist = line_nodes{n}.patienceDistributions{1, r};
2064 if ~isa(patDist, 'Disabled')
2065 impType = ImpatienceType.RENEGING;
2066 if ~isempty(line_nodes{n}.impatienceTypes) && ...
2067 r <= length(line_nodes{n}.impatienceTypes) && ...
2068 ~isempty(line_nodes{n}.impatienceTypes{1, r})
2069 impType = line_nodes{n}.impatienceTypes{1, r};
2070 end
2071 jImpType = jline.lang.constant.ImpatienceType.fromID(int32(impType));
2072 jnodes{n}.setPatience(jclasses{r}, jImpType, JLINE.from_line_distribution(patDist));
2073 end
2074 end
2075 % Balking (state-based, queue-length/expected-wait thresholds)
2076 if ~isempty(line_nodes{n}.balkingStrategies) && ...
2077 r <= length(line_nodes{n}.balkingStrategies) && ...
2078 ~isempty(line_nodes{n}.balkingStrategies{1, r})
2079 balkStrat = line_nodes{n}.balkingStrategies{1, r};
2080 switch balkStrat
2081 case BalkingStrategy.QUEUE_LENGTH
2082 jBalkStrat = jline.lang.constant.BalkingStrategy.QUEUE_LENGTH;
2083 case BalkingStrategy.EXPECTED_WAIT
2084 jBalkStrat = jline.lang.constant.BalkingStrategy.EXPECTED_WAIT;
2085 case BalkingStrategy.COMBINED
2086 jBalkStrat = jline.lang.constant.BalkingStrategy.COMBINED;
2087 otherwise
2088 jBalkStrat = jline.lang.constant.BalkingStrategy.QUEUE_LENGTH;
2089 end
2090 thresholds = line_nodes{n}.balkingThresholds{1, r};
2091 jThresholds = java.util.ArrayList();
2092 for ti = 1:length(thresholds)
2093 th = thresholds{ti};
2094 minJobs = th{1};
2095 maxJobs = th{2};
2096 if isinf(maxJobs)
2097 maxJobs = java.lang.Integer.MAX_VALUE;
2098 end
2099 jThresholds.add(javaObject('jline.lang.constant.BalkingThreshold', int32(minJobs), int32(maxJobs), th{3}));
2100 end
2101 jnodes{n}.setBalking(jclasses{r}, jBalkStrat, jThresholds);
2102 end
2103 % Retrial (orbit + retrial delay distribution)
2104 if ~isempty(line_nodes{n}.retrialDelays) && ...
2105 r <= length(line_nodes{n}.retrialDelays) && ...
2106 ~isempty(line_nodes{n}.retrialDelays{1, r})
2107 retDist = line_nodes{n}.retrialDelays{1, r};
2108 if ~isa(retDist, 'Disabled')
2109 maxAttempts = -1;
2110 if ~isempty(line_nodes{n}.retrialMaxAttempts) && r <= length(line_nodes{n}.retrialMaxAttempts)
2111 maxAttempts = line_nodes{n}.retrialMaxAttempts(r);
2112 end
2113 jnodes{n}.setRetrial(jclasses{r}, JLINE.from_line_distribution(retDist), int32(maxAttempts));
2114 end
2115 end
2116 % Orbit impatience (abandonment from the retrial orbit)
2117 if ~isempty(line_nodes{n}.orbitImpatienceDistributions) && ...
2118 r <= length(line_nodes{n}.orbitImpatienceDistributions) && ...
2119 ~isempty(line_nodes{n}.orbitImpatienceDistributions{1, r})
2120 orbDist = line_nodes{n}.orbitImpatienceDistributions{1, r};
2121 if ~isa(orbDist, 'Disabled')
2122 jnodes{n}.setOrbitImpatience(jclasses{r}, JLINE.from_line_distribution(orbDist));
2123 end
2124 end
2125 % Batch rejection probability (retrial queues)
2126 if ~isempty(line_nodes{n}.batchRejectProb) && ...
2127 r <= length(line_nodes{n}.batchRejectProb) && ...
2128 line_nodes{n}.batchRejectProb(r) > 0
2129 jnodes{n}.setBatchRejectProbability(jclasses{r}, line_nodes{n}.batchRejectProb(r));
2130 end
2131 end
2132 end
2133
2134 % Transfer pass-and-swap (PAS) parameters: the total service rate
2135 % function mu(c) and the swap graph. Requires classes to be created
2136 % first (the JAR setServiceRateFunction derives per-class rates and
2137 % setSwapGraph validates against the class count).
2138 for n = 1: length(jnodes)
2139 if isempty(jnodes{n})
2140 continue;
2141 end
2142 if ~isa(line_nodes{n}, 'Queue') || ...
2143 (line_nodes{n}.schedStrategy ~= SchedStrategy.PAS && line_nodes{n}.schedStrategy ~= SchedStrategy.OI)
2144 continue;
2145 end
2146 if isempty(line_nodes{n}.svcRateFun)
2147 line_error(mfilename, sprintf('PAS queue ''%s'' has no service rate function mu(c); set it via setService(@(c) ...).', line_nodes{n}.getName));
2148 end
2149 if isinf(line_nodes{n}.cap)
2150 line_error(mfilename, sprintf('PAS queue ''%s'' requires a finite capacity for JLINE conversion; set it via setCap(...).', line_nodes{n}.getName));
2151 end
2152 jSerFun = JLINE.pas_handle_to_serializablefun(line_nodes{n}.svcRateFun, sn.nclasses, line_nodes{n}.cap);
2153 jnodes{n}.setServiceRateFunction(jSerFun);
2154 % Transfer the swap graph if explicitly set (otherwise the JAR
2155 % defaults to a complete graph at struct refresh, as MATLAB does)
2156 if ~isempty(line_nodes{n}.swapGraph)
2157 jnodes{n}.setSwapGraph(JLINE.from_line_matrix(line_nodes{n}.swapGraph));
2158 end
2159 end
2160
2161 % Transfer heterogeneous server types and their per-(serverType,class)
2162 % service distributions. Requires classes to be created first.
2163 for n = 1: length(jnodes)
2164 if isempty(jnodes{n})
2165 continue;
2166 end
2167 if ~isa(line_nodes{n}, 'Queue') || isempty(line_nodes{n}.serverTypes)
2168 continue;
2169 end
2170 % Map MATLAB class names to Java class objects
2171 % Add each server type with its compatible classes
2172 jServerTypes = cell(1, length(line_nodes{n}.serverTypes));
2173 for st = 1:length(line_nodes{n}.serverTypes)
2174 serverType = line_nodes{n}.serverTypes{st};
2175 jCompat = java.util.ArrayList();
2176 compatClasses = serverType.getCompatibleClasses();
2177 for cc = 1:length(compatClasses)
2178 jCompat.add(jclasses{compatClasses{cc}.index});
2179 end
2180 jST = javaObject('jline.lang.constant.ServerType', serverType.getName(), int32(serverType.getNumOfServers()), jCompat);
2181 jnodes{n}.addServerType(jST);
2182 jServerTypes{st} = jST;
2183 end
2184 % Set heterogeneous scheduling policy
2185 switch line_nodes{n}.heteroSchedPolicy
2186 case HeteroSchedPolicy.ORDER
2187 jHetPol = jline.lang.constant.HeteroSchedPolicy.ORDER;
2188 case HeteroSchedPolicy.ALIS
2189 jHetPol = jline.lang.constant.HeteroSchedPolicy.ALIS;
2190 case HeteroSchedPolicy.ALFS
2191 jHetPol = jline.lang.constant.HeteroSchedPolicy.ALFS;
2192 case HeteroSchedPolicy.FAIRNESS
2193 jHetPol = jline.lang.constant.HeteroSchedPolicy.FAIRNESS;
2194 case HeteroSchedPolicy.FSF
2195 jHetPol = jline.lang.constant.HeteroSchedPolicy.FSF;
2196 case HeteroSchedPolicy.RAIS
2197 jHetPol = jline.lang.constant.HeteroSchedPolicy.RAIS;
2198 otherwise
2199 jHetPol = jline.lang.constant.HeteroSchedPolicy.ORDER;
2200 end
2201 jnodes{n}.setHeteroSchedPolicy(jHetPol);
2202 % Set per-(serverType, class) service distributions
2203 for st = 1:length(line_nodes{n}.serverTypes)
2204 serverType = line_nodes{n}.serverTypes{st};
2205 for r = 1:length(line_classes)
2206 hetDist = line_nodes{n}.getHeteroService(line_classes{r}, serverType);
2207 if ~isempty(hetDist)
2208 jnodes{n}.setService(jclasses{r}, jServerTypes{st}, JLINE.from_line_distribution(hetDist));
2209 end
2210 end
2211 end
2212 end
2213
2214 for n = 1: length(jnodes)
2215 if isempty(jnodes{n})
2216 continue; % Skip nodes that were not converted
2217 end
2218 if isa(line_nodes{n},"ClassSwitch") && ~line_nodes{n}.autoAdded
2219 % Only set csMatrix for user-defined ClassSwitch nodes (not auto-added)
2220 JLINE.set_csMatrix(line_nodes{n}, jnodes{n}, jclasses);
2221 elseif isa(line_nodes{n},"Join")
2222 jnodes{n}.initJoinJobClasses();
2223 % Restore RAND routing for all classes, matching ClosedClass/OpenClass
2224 % constructor behavior (initJoinJobClasses sets DISABLED by default)
2225 for r = 1 : sn.nclasses
2226 jnodes{n}.setRouting(jclasses{r}, jline.lang.constant.RoutingStrategy.RAND);
2227 end
2228 % Transfer per-class join strategy and required count (quorum
2229 % joins). Must run after initJoinJobClasses, which replaces
2230 % the Joiner section and resets both to their STD/-1 defaults.
2231 for r = 1 : sn.nclasses
2232 if length(line_nodes{n}.input.joinStrategy) >= r && ~isempty(line_nodes{n}.input.joinStrategy{r}) ...
2233 && line_nodes{n}.input.joinStrategy{r} == JoinStrategy.PARTIAL
2234 jnodes{n}.setStrategy(jclasses{r}, jline.lang.constant.JoinStrategy.PARTIAL);
2235 end
2236 if length(line_nodes{n}.input.joinRequired) >= r && ~isempty(line_nodes{n}.input.joinRequired{r}) ...
2237 && line_nodes{n}.input.joinRequired{r} > 0
2238 jnodes{n}.setRequired(jclasses{r}, line_nodes{n}.input.joinRequired{r});
2239 end
2240 end
2241 elseif isa(line_nodes{n},"Cache")
2242 hitC = line_nodes{n}.server.hitClass;
2243 missC = line_nodes{n}.server.missClass;
2244 for r = 1 : sn.nclasses
2245 % Transfer per-class cache setup based on what each class
2246 % actually has: read (input) classes carry a popularity plus a
2247 % hit and miss class; the auto-generated retrieval classes carry
2248 % a one-hot popularity and a miss class only. Gating on hitClass
2249 % alone (as before) dropped the retrieval classes' read
2250 % distribution, leaving the JAR cache state handler with a null
2251 % popularity (NPE in AfterEventCache during SSA/CTMC).
2252 hasPop = numel(line_nodes{n}.popularity) >= r ...
2253 && ~isempty(line_nodes{n}.popularity{r}) ...
2254 && ~isa(line_nodes{n}.popularity{r},'Disabled');
2255 hasHit = length(hitC) >= r && full(hitC(r)) > 0;
2256 hasMiss = length(missC) >= r && full(missC(r)) > 0;
2257 if hasPop
2258 jnodes{n}.setRead(jclasses{r}, JLINE.from_line_distribution(line_nodes{n}.popularity{r}));
2259 end
2260 if hasHit
2261 jnodes{n}.setHitClass(jclasses{r}, jclasses{full(hitC(r))});
2262 end
2263 if hasMiss
2264 jnodes{n}.setMissClass(jclasses{r}, jclasses{full(missC(r))});
2265 end
2266 end
2267 % Transfer accessProb from MATLAB to Java
2268 if ~isempty(line_nodes{n}.accessProb)
2269 accessProbMat = line_nodes{n}.accessProb;
2270 [K1, K2] = size(accessProbMat);
2271 jAccessProb = javaArray('jline.util.matrix.Matrix', K1, K2);
2272 for k1 = 1:K1
2273 for k2 = 1:K2
2274 if ~isempty(accessProbMat{k1, k2})
2275 jAccessProb(k1, k2) = JLINE.from_line_matrix(accessProbMat{k1, k2});
2276 end
2277 end
2278 end
2279 jnodes{n}.setAccessProb(jAccessProb);
2280 end
2281 % Reconstruct the delayed-hit retrieval system bookkeeping on the
2282 % Java cache. line_to_jline already transfers the retrieval queues,
2283 % their service and the routing; attachRetrievalSystem restores the
2284 % cache-internal state (retrievalSystemCapacity, queue indices,
2285 % retrieval-class set and per-item mapping) the JAR solvers read to
2286 % detect the retrieval system. Without it the JAR routes to the plain
2287 % cache analyzer (no delayed hits; NC errors when nItems < cap+2).
2288 if ~isempty(line_nodes{n}.retrievalSystemQueueIndices) ...
2289 && line_nodes{n}.retrievalSystemQueueIndices.Count > 0
2290 rsqi = line_nodes{n}.retrievalSystemQueueIndices;
2291 rclasses = line_nodes{n}.server.retrievalClasses; % [nItems x nclasses], 1-based or -1
2292 nItemsRS = size(rclasses, 1);
2293 keysRS = keys(rsqi);
2294 for kk = 1:numel(keysRS)
2295 jobinIdx0 = double(keysRS{kk}); % 0-based arrival class index
2296 jobinClassObj = jclasses{jobinIdx0 + 1};
2297 queueIdxs = rsqi(keysRS{kk}); % 1-based MATLAB node indices
2298 qList = javaObject('java.util.ArrayList');
2299 for q = 1:numel(queueIdxs)
2300 qList.add(java.lang.Integer(int32(queueIdxs(q) - 1))); % 0-based node index
2301 end
2302 rcArr = javaArray('jline.lang.JobClass', nItemsRS);
2303 for i = 1:nItemsRS
2304 rIdx = rclasses(i, jobinIdx0 + 1);
2305 if rIdx >= 1
2306 rcArr(i) = jclasses{rIdx};
2307 end
2308 end
2309 jnodes{n}.attachRetrievalSystem(jobinClassObj, qList, rcArr);
2310 end
2311 end
2312 elseif isa(line_nodes{n}, "Place") && line_nodes{n}.isQueueing()
2313 % Queueing place (QPN embedded queue): marshal the per-class
2314 % service processes, embedded-server count and departure
2315 % discipline. Must run after classes are created. The Place was
2316 % already reconstructed with its scheduling strategy in
2317 % from_line_node; setService flips it to queueing on the JAR side.
2318 for r = 1:sn.nclasses
2319 if numel(line_nodes{n}.serviceProcess) >= r && ~isempty(line_nodes{n}.serviceProcess{r})
2320 jdist = JLINE.from_line_distribution(line_nodes{n}.serviceProcess{r});
2321 jnodes{n}.setService(jclasses{r}, jdist);
2322 end
2323 end
2324 nsrv = line_nodes{n}.numberOfServers;
2325 if ~isinf(nsrv)
2326 jnodes{n}.setNumberOfServers(int32(nsrv));
2327 end
2328 for r = 1:sn.nclasses
2329 if numel(line_nodes{n}.departureDiscipline) >= r && line_nodes{n}.departureDiscipline(r) > 0
2330 jnodes{n}.setDepartureDiscipline(jclasses{r}, ...
2331 jline.lang.constant.DepartureDiscipline.fromID(line_nodes{n}.departureDiscipline(r)));
2332 end
2333 end
2334 elseif isa(line_nodes{n}, "Transition")
2335 % First, add modes (must be done after classes are created)
2336 for m = 1:line_nodes{n}.getNumberOfModes()
2337 modeName = line_nodes{n}.modeNames{m};
2338 jmode = jnodes{n}.addMode(modeName);
2339 % Set timing strategy
2340 switch line_nodes{n}.timingStrategies(m)
2341 case TimingStrategy.TIMED
2342 jnodes{n}.setTimingStrategy(jmode, jline.lang.constant.TimingStrategy.TIMED);
2343 case TimingStrategy.IMMEDIATE
2344 jnodes{n}.setTimingStrategy(jmode, jline.lang.constant.TimingStrategy.IMMEDIATE);
2345 end
2346 % Set distribution
2347 jnodes{n}.setDistribution(jmode, JLINE.from_line_distribution(line_nodes{n}.distributions{m}));
2348 % Set firing weights and priorities
2349 jnodes{n}.setFiringWeights(jmode, line_nodes{n}.firingWeights(m));
2350 jnodes{n}.setFiringPriorities(jmode, int32(line_nodes{n}.firingPriorities(m)));
2351 % Set number of servers
2352 nsrv = line_nodes{n}.numberOfServers(m);
2353 if isinf(nsrv)
2354 jnodes{n}.setNumberOfServers(jmode, java.lang.Integer(intmax('int32')));
2355 else
2356 jnodes{n}.setNumberOfServers(jmode, java.lang.Integer(int32(nsrv)));
2357 end
2358 end
2359 % Now set enabling conditions, inhibiting conditions, and firing outcomes
2360 jmodes = jnodes{n}.getModes();
2361 for m = 1:line_nodes{n}.getNumberOfModes()
2362 jmode = jmodes.get(m-1);
2363 enabCond = line_nodes{n}.enablingConditions{m};
2364 inhibCond = line_nodes{n}.inhibitingConditions{m};
2365 firingOut = line_nodes{n}.firingOutcomes{m};
2366 % The condition matrices are sized (nnodes x nclasses)
2367 % at addMode time using the node count then in existence,
2368 % so a transition created before later nodes has fewer
2369 % rows than length(line_nodes). Bound each access by the
2370 % matrix's own row count to avoid over-indexing.
2371 for r = 1:sn.nclasses
2372 for i = 1:length(line_nodes)
2373 % Set enabling conditions
2374 if i <= size(enabCond, 1) && enabCond(i, r) > 0 && isa(line_nodes{i}, 'Place')
2375 jnodes{n}.setEnablingConditions(jmode, jclasses{r}, jnodes{i}, enabCond(i, r));
2376 end
2377 % Set inhibiting conditions
2378 if i <= size(inhibCond, 1) && inhibCond(i, r) < Inf && isa(line_nodes{i}, 'Place')
2379 jnodes{n}.setInhibitingConditions(jmode, jclasses{r}, jnodes{i}, inhibCond(i, r));
2380 end
2381 % Set firing outcomes
2382 if i <= size(firingOut, 1) && firingOut(i, r) ~= 0
2383 jnodes{n}.setFiringOutcome(jmode, jclasses{r}, jnodes{i}, firingOut(i, r));
2384 end
2385 end
2386 end
2387 end
2388 end
2389 end
2390
2391 % Transfer the MATLAB-side node states (e.g. a closed pass-and-swap
2392 % station's required initial placement) BEFORE from_line_links, whose
2393 % internal getStruct call would otherwise trigger the JAR's initDefault
2394 % validation and error on a missing PAS placement before the states
2395 % have been provided.
2396 for n = 1: length(line_nodes)
2397 if isempty(jnodes{n})
2398 continue;
2399 end
2400 if line_nodes{n}.isStateful
2401 jnodes{n}.setState(JLINE.from_line_matrix(line_nodes{n}.getState));
2402 jnodes{n}.setStateSpace(JLINE.from_line_matrix(line_nodes{n}.getStateSpace));
2403 jnodes{n}.setStatePrior(JLINE.from_line_matrix(line_nodes{n}.getStatePrior));
2404 end
2405 end
2406
2407 % Assume JLINE and LINE network are both created via link
2408 JLINE.from_line_links(model, jnetwork);
2409
2410 % Transfer finite capacity regions from MATLAB to Java
2411 if ~isempty(model.regions)
2412 for f = 1:length(model.regions)
2413 fcr = model.regions{f};
2414 % Convert MATLAB node list to Java list
2415 javaNodeList = java.util.ArrayList();
2416 for i = 1:length(fcr.nodes)
2417 matlabNode = fcr.nodes{i};
2418 % Find corresponding Java node by name
2419 nodeName = matlabNode.getName();
2420 for j = 1:length(line_nodes)
2421 if strcmp(line_nodes{j}.getName(), nodeName) && ~isempty(jnodes{j})
2422 javaNodeList.add(jnodes{j});
2423 break;
2424 end
2425 end
2426 end
2427 % Create Java FCR
2428 jfcr = jnetwork.addRegion(javaNodeList);
2429 % Set global max jobs
2430 if fcr.globalMaxJobs > 0 && ~isinf(fcr.globalMaxJobs)
2431 jfcr.setGlobalMaxJobs(fcr.globalMaxJobs);
2432 end
2433 % Set global memory budget (KV-cache cap in the LLM model).
2434 % Passed unrounded: fractional budgets are legitimate now
2435 % that classSize footprints may be fractional.
2436 if fcr.globalMaxMemory > 0 && ~isinf(fcr.globalMaxMemory)
2437 jfcr.setGlobalMaxMemory(fcr.globalMaxMemory);
2438 end
2439 % Set per-class max jobs, memory, footprint, weight, drop rules
2440 for r = 1:length(line_classes)
2441 if length(fcr.classMaxJobs) >= r && fcr.classMaxJobs(r) > 0 && ~isinf(fcr.classMaxJobs(r))
2442 jfcr.setClassMaxJobs(jclasses{r}, fcr.classMaxJobs(r));
2443 end
2444 if length(fcr.classMaxMemory) >= r && fcr.classMaxMemory(r) > 0 && ~isinf(fcr.classMaxMemory(r))
2445 jfcr.setClassMaxMemory(jclasses{r}, round(fcr.classMaxMemory(r)));
2446 end
2447 % classSize is the per-class memory footprint charged against
2448 % the global memory budget; default is 1. Passed unrounded
2449 % (Java stores it as double) so fractional footprints match
2450 % the JMT XML writer; zero is a legitimate explicit footprint.
2451 % Default-valued entries are skipped to avoid redundant
2452 % MATLAB-to-Java calls (both sides default to 1).
2453 if length(fcr.classSize) >= r && isfinite(fcr.classSize(r)) && fcr.classSize(r) >= 0 && fcr.classSize(r) ~= 1
2454 jfcr.setClassSize(jclasses{r}, fcr.classSize(r));
2455 end
2456 if length(fcr.classWeight) >= r && isfinite(fcr.classWeight(r)) && fcr.classWeight(r) > 0 && fcr.classWeight(r) ~= 1
2457 jfcr.setClassWeight(jclasses{r}, fcr.classWeight(r));
2458 end
2459 if length(fcr.dropRule) >= r
2460 % Convert MATLAB DropStrategy numeric to Java DropStrategy enum
2461 jDropStrategy = jline.lang.constant.DropStrategy.fromID(fcr.dropRule(r));
2462 jfcr.setDropRule(jclasses{r}, jDropStrategy);
2463 end
2464 end
2465 % Transfer linear constraints if set
2466 if fcr.hasLinearConstraints()
2467 jA = JLINE.from_line_matrix(fcr.constraintA);
2468 jb = JLINE.from_line_matrix(fcr.constraintB);
2469 jfcr.setLinearConstraints(jA, jb);
2470 end
2471 end
2472 end
2473
2474 % Transfer CTMC reward definitions. The MATLAB reward functions are
2475 % opaque handles evaluated on rows of the aggregated state space
2476 % [n_{1,1},...,n_{M,K}] (station-major), so they are marshalled by
2477 % tabulating their values over the enumerable superset of that
2478 % domain into a jline TabulatedRewardFunction.
2479 if (isstruct(sn) && isfield(sn, 'reward') || isprop(sn, 'reward')) && ~isempty(sn.reward)
2480 for ri = 1:length(sn.reward)
2481 jnetwork.setReward(sn.reward{ri}.name, JLINE.reward_handle_to_tabulatedfun(sn.reward{ri}.fn, sn));
2482 end
2483 end
2484
2485 % Transfer the MATLAB-side node states BEFORE initDefault so that the
2486 % JAR's initDefault sees user-provided initial states (e.g. a closed
2487 % pass-and-swap station's required initial placement) rather than
2488 % erroring or fabricating a default for them. initDefault then only
2489 % initializes the stateful nodes still lacking a state.
2490 for n = 1: length(line_nodes)
2491 if isempty(jnodes{n})
2492 continue; % Skip nodes that were not converted
2493 end
2494 if line_nodes{n}.isStateful
2495 jnodes{n}.setState(JLINE.from_line_matrix(line_nodes{n}.getState));
2496 jnodes{n}.setStateSpace(JLINE.from_line_matrix(line_nodes{n}.getStateSpace));
2497 jnodes{n}.setStatePrior(JLINE.from_line_matrix(line_nodes{n}.getStatePrior));
2498 end
2499 end
2500 jnetwork.initDefault;
2501 % Force struct refresh so sn.state reflects updated node states
2502 jnetwork.setHasStruct(false);
2503
2504 end
2505
2506 function jnetwork = line_to_jline(model)
2507 jnetwork = LINE2JLINE(model);
2508 end
2509
2510 function model = jline_to_line(jnetwork)
2511 if isa(jnetwork,'JNetwork')
2512 jnetwork = jnetwork.obj;
2513 end
2514 %javaaddpath(jar_loc);
2515 model = Network(char(jnetwork.getName));
2516 network_nodes = jnetwork.getNodes;
2517 job_classes = jnetwork.getClasses;
2518
2519 line_nodes = cell(network_nodes.size,1);
2520 line_classes = cell(job_classes.size,1);
2521
2522
2523 for n = 1 : network_nodes.size
2524 if ~isa(network_nodes.get(n-1), 'jline.lang.nodes.ClassSwitch')
2525 line_nodes{n} = JLINE.from_jline_node(network_nodes.get(n-1), model, job_classes);
2526 end
2527 end
2528
2529 for n = 1 : job_classes.size
2530 line_classes{n} = JLINE.from_jline_class(job_classes.get(n-1), model);
2531 end
2532
2533 % Deferred signal target association (forJobClass), once all
2534 % classes exist
2535 for n = 1 : job_classes.size
2536 jc = job_classes.get(n-1);
2537 if isa(jc, 'jline.lang.ClosedSignal') || isa(jc, 'jline.lang.OpenSignal') || isa(jc, 'jline.lang.Signal')
2538 jtarget = jc.getTargetJobClass();
2539 if ~isempty(jtarget)
2540 tname = char(jtarget.getName);
2541 for m = 1 : job_classes.size
2542 if strcmp(line_classes{m}.name, tname)
2543 line_classes{n}.forJobClass(line_classes{m});
2544 break;
2545 end
2546 end
2547 end
2548 end
2549 end
2550
2551 for n = 1 : network_nodes.size
2552 if isa(network_nodes.get(n-1), 'jline.lang.nodes.ClassSwitch')
2553 line_nodes{n} = JLINE.from_jline_node(network_nodes.get(n-1), model, job_classes);
2554 end
2555 end
2556
2557 % Deferred Fork/Join linking: set joinOf on Join nodes
2558 for n = 1 : network_nodes.size
2559 jnode = network_nodes.get(n-1);
2560 if isa(jnode, 'jline.lang.nodes.Join') && ~isempty(jnode.joinOf)
2561 forkName = char(jnode.joinOf.getName);
2562 for m = 1 : network_nodes.size
2563 if ~isempty(line_nodes{m}) && isa(line_nodes{m}, 'Fork') && strcmp(line_nodes{m}.name, forkName)
2564 line_nodes{n}.joinOf = line_nodes{m};
2565 break;
2566 end
2567 end
2568 end
2569 end
2570
2571 % Deferred Cache setup: set hitClass, missClass, popularity
2572 for n = 1 : network_nodes.size
2573 jnode = network_nodes.get(n-1);
2574 if isa(jnode, 'jline.lang.nodes.Cache') && isa(line_nodes{n}, 'Cache')
2575 cacheNode = line_nodes{n};
2576 % hitClass and missClass are stored as index vectors
2577 hitClassVec = JLINE.from_jline_matrix(jnode.getHitClass());
2578 missClassVec = JLINE.from_jline_matrix(jnode.getMissClass());
2579 for r = 1:job_classes.size
2580 hitIdx = hitClassVec(r);
2581 if hitIdx >= 0 && (hitIdx + 1) <= job_classes.size
2582 cacheNode.setHitClass(line_classes{r}, line_classes{hitIdx + 1});
2583 end
2584 missIdx = missClassVec(r);
2585 if missIdx >= 0 && (missIdx + 1) <= job_classes.size
2586 cacheNode.setMissClass(line_classes{r}, line_classes{missIdx + 1});
2587 end
2588 end
2589 % Popularity distributions
2590 for r = 1:job_classes.size
2591 try
2592 popDist = jnode.popularityGet(0, r-1);
2593 if ~isempty(popDist) && popDist.isDiscrete()
2594 matlabDist = JLINE.from_jline_distribution(popDist);
2595 if ~isempty(matlabDist)
2596 cacheNode.setRead(line_classes{r}, matlabDist);
2597 end
2598 end
2599 catch
2600 % No popularity for this class
2601 end
2602 end
2603 end
2604 end
2605
2606 for n = 1 : network_nodes.size
2607 JLINE.set_line_service(network_nodes.get(n-1), line_nodes{n}, job_classes, line_classes);
2608 end
2609
2610 % Configure Transition modes (distributions, enabling/inhibiting/firing, etc.)
2611 for n = 1 : network_nodes.size
2612 jnode = network_nodes.get(n-1);
2613 if isa(jnode, 'jline.lang.nodes.Transition') && isa(line_nodes{n}, 'Transition')
2614 tnode = line_nodes{n};
2615 jmodes = jnode.getModes();
2616 nmodes = jmodes.size();
2617 for m = 1 : nmodes
2618 jmode = jmodes.get(m-1);
2619 modeName = char(jmode.getName());
2620 % addMode if not yet present (Transition starts with one default mode)
2621 if m > tnode.getNumberOfModes()
2622 tnode.addMode(modeName);
2623 else
2624 tnode.setModeNames(m, modeName);
2625 end
2626 % Timing strategy
2627 ts = jnode.timingStrategies.get(jmode);
2628 if ~isempty(ts)
2629 tsName = char(ts.name());
2630 if strcmp(tsName, 'IMMEDIATE')
2631 tnode.setTimingStrategy(m, TimingStrategy.IMMEDIATE);
2632 else
2633 tnode.setTimingStrategy(m, TimingStrategy.TIMED);
2634 end
2635 end
2636 % Distribution
2637 jdist = jnode.getFiringDistribution(jmode);
2638 if ~isempty(jdist)
2639 matlabDist = JLINE.from_jline_distribution(jdist);
2640 if ~isempty(matlabDist)
2641 tnode.setDistribution(m, matlabDist);
2642 end
2643 end
2644 % Number of servers
2645 numSrv = jnode.getNumberOfModeServers(jmode);
2646 if numSrv == intmax('int32') || numSrv == intmax('int64')
2647 tnode.setNumberOfServers(m, Inf);
2648 else
2649 tnode.setNumberOfServers(m, double(numSrv));
2650 end
2651 % Firing priority and weight (read from matrices by mode index)
2652 if jnode.firingPriorities.getNumElements() > (m-1)
2653 tnode.setFiringPriorities(m, jnode.firingPriorities.get(m-1));
2654 end
2655 if jnode.firingWeights.getNumElements() > (m-1)
2656 tnode.setFiringWeights(m, jnode.firingWeights.get(m-1));
2657 end
2658 % Enabling conditions
2659 ecMat = jnode.enablingConditions.get(jmode);
2660 if ~isempty(ecMat)
2661 nrows = ecMat.getNumRows();
2662 ncols = ecMat.getNumCols();
2663 for ni = 1 : nrows
2664 for ci = 1 : ncols
2665 val = ecMat.get(ni-1, ci-1);
2666 if val > 0 && ni <= length(line_nodes) && isa(line_nodes{ni}, 'Place')
2667 tnode.setEnablingConditions(m, ci, line_nodes{ni}, val);
2668 end
2669 end
2670 end
2671 end
2672 % Inhibiting conditions
2673 icMat = jnode.inhibitingConditions.get(jmode);
2674 if ~isempty(icMat)
2675 nrows = icMat.getNumRows();
2676 ncols = icMat.getNumCols();
2677 for ni = 1 : nrows
2678 for ci = 1 : ncols
2679 val = icMat.get(ni-1, ci-1);
2680 if isfinite(val) && val > 0 && ni <= length(line_nodes) && isa(line_nodes{ni}, 'Place')
2681 tnode.setInhibitingConditions(m, ci, line_nodes{ni}, val);
2682 end
2683 end
2684 end
2685 end
2686 % Firing outcomes
2687 foMat = jnode.firingOutcomes.get(jmode);
2688 if ~isempty(foMat)
2689 nrows = foMat.getNumRows();
2690 ncols = foMat.getNumCols();
2691 for ni = 1 : nrows
2692 for ci = 1 : ncols
2693 val = foMat.get(ni-1, ci-1);
2694 if val ~= 0 && ni <= length(line_nodes)
2695 tnode.setFiringOutcome(m, ci, line_nodes{ni}, val);
2696 end
2697 end
2698 end
2699 end
2700 end
2701 end
2702 end
2703
2704 % Check for state-dependent routing (RROBIN, WRROBIN, JSQ)
2705 % These cannot go through link(P) because it overrides routing strategies
2706 hasSDRouting = false;
2707 for n = 1 : network_nodes.size
2708 jnode = network_nodes.get(n-1);
2709 output_strategies = jnode.getOutputStrategies();
2710 for m = 1 : output_strategies.size()
2711 rs = char(output_strategies.get(m-1).getRoutingStrategy);
2712 if any(strcmp(rs, {'RROBIN','WRROBIN','JSQ','KCHOICES'}))
2713 hasSDRouting = true;
2714 break;
2715 end
2716 end
2717 if hasSDRouting; break; end
2718 end
2719
2720 if hasSDRouting
2721 % State-dependent routing: use addLink + setRouting
2722 model = JLINE.from_jline_routing(model, jnetwork);
2723 elseif ~isempty(jnetwork.getStruct.rtorig)
2724 % Use link() method
2725 model = JLINE.from_jline_links(model, jnetwork);
2726 else
2727 % Do not use link() method
2728 model = JLINE.from_jline_routing(model, jnetwork);
2729 end
2730
2731 % Restore initial state on Place nodes after linking
2732 for n = 1 : network_nodes.size
2733 jnode = network_nodes.get(n-1);
2734 if isa(jnode, 'jline.lang.nodes.Place') && ~isempty(line_nodes{n}) && isa(line_nodes{n}, 'Place')
2735 jst = jnode.getState();
2736 if ~isempty(jst) && ~jst.isEmpty()
2737 line_nodes{n}.setState(JLINE.from_jline_matrix(jst));
2738 end
2739 end
2740 end
2741
2742 % Transfer finite capacity regions from Java to MATLAB (reverse of
2743 % the block in from_line_network; without this the FCR is silently
2744 % dropped and the reconstructed model solves unconstrained).
2745 jregions = jnetwork.getRegions();
2746 for f = 1 : jregions.size()
2747 jfcr = jregions.get(f-1);
2748 jrnodes = jfcr.getNodes();
2749 regionNodes = cell(1, jrnodes.size());
2750 for i = 1 : jrnodes.size()
2751 rname = char(jrnodes.get(i-1).getName());
2752 for n = 1 : length(line_nodes)
2753 if ~isempty(line_nodes{n}) && strcmp(line_nodes{n}.getName(), rname)
2754 regionNodes{i} = line_nodes{n};
2755 break;
2756 end
2757 end
2758 end
2759 fcr = model.addRegion(regionNodes);
2760 gmj = jfcr.getGlobalMaxJobs();
2761 if gmj > 0
2762 fcr.setGlobalMaxJobs(gmj);
2763 end
2764 gmm = jfcr.getGlobalMaxMemory();
2765 if gmm > 0
2766 fcr.setGlobalMaxMemory(gmm);
2767 end
2768 for r = 1 : length(line_classes)
2769 jclass = job_classes.get(r-1);
2770 cmj = jfcr.getClassMaxJobs(jclass);
2771 if cmj > 0
2772 fcr.setClassMaxJobs(line_classes{r}, cmj);
2773 end
2774 cmm = jfcr.getClassMaxMemory(jclass);
2775 if cmm > 0
2776 fcr.setClassMaxMemory(line_classes{r}, cmm);
2777 end
2778 cw = jfcr.getClassWeight(jclass);
2779 if isfinite(cw) && cw > 0 && cw ~= 1
2780 fcr.setClassWeight(line_classes{r}, cw);
2781 end
2782 cs = jfcr.getClassSize(jclass);
2783 if isfinite(cs) && cs >= 0 && cs ~= 1
2784 fcr.setClassSize(line_classes{r}, cs);
2785 end
2786 % Java getID() uses the MATLAB numeric ids (fromID accepts
2787 % them on the forward path), so the id round-trips directly.
2788 fcr.setDropRule(line_classes{r}, jfcr.getDropStrategy(jclass).getID());
2789 end
2790 jlin = jfcr.getLinearConstraints();
2791 if ~isempty(jlin)
2792 fcr.setConstraint(JLINE.from_jline_matrix(jlin(1)), JLINE.from_jline_matrix(jlin(2)));
2793 end
2794 end
2795 end
2796
2797 function matrix = arraylist_to_matrix(jline_matrix)
2798 if isempty(jline_matrix)
2799 matrix = [];
2800 else
2801 matrix = zeros(jline_matrix.size(), 1);
2802 for row = 1:jline_matrix.size()
2803 matrix(row, 1) = jline_matrix.get(row-1);
2804 end
2805 end
2806 end
2807
2808 function matrix = from_jline_matrix(jline_matrix)
2809 if isempty(jline_matrix)
2810 matrix = [];
2811 else
2812 matrix = zeros(jline_matrix.getNumRows(), jline_matrix.getNumCols());
2813 for row = 1:jline_matrix.getNumRows()
2814 for col = 1:jline_matrix.getNumCols()
2815 val = jline_matrix.get(row-1, col-1);
2816 if (val >= 33333333 && val <= 33333334)
2817 matrix(row, col) = GlobalConstants.Immediate;
2818 elseif (val >= -33333334 && val <= -33333333)
2819 matrix(row, col) = -GlobalConstants.Immediate;
2820 elseif (val >= 2147483647 - 1) % Integer.MAX_VALUE with -1 tolerance
2821 matrix(row, col) = Inf;
2822 elseif (val <= -2147483648 + 1) % Integer.MIN_VALUE with +1 tolerance
2823 matrix(row, col) = -Inf;
2824 else
2825 matrix(row, col) = val;
2826 end
2827 end
2828 end
2829 end
2830 end
2831
2832 function jdist = from_line_lqn_dist(ptype, dmean, dscv, dparams, dproc)
2833 % JDIST = FROM_LINE_LQN_DIST(PTYPE, DMEAN, DSCV, DPROC)
2834 % Rebuild a JAR distribution from the LayeredNetworkStruct fields of
2835 % an LQN distribution, keeping its variability. Passing the mean
2836 % alone to a setSomething(double) overload rebuilds it as Exp(1/mean)
2837 % with SCV 1, which silently discards everything above the first
2838 % moment: an Erlang setup and an exponential one of the same mean
2839 % would reach the JAR as the same process.
2840 switch ptype
2841 case ProcessType.IMMEDIATE
2842 jdist = jline.lang.processes.Immediate;
2843 case ProcessType.EXP
2844 jdist = jline.lang.processes.Exp(1/dmean);
2845 case ProcessType.ERLANG
2846 jdist = jline.lang.processes.Erlang.fitMeanAndSCV(dmean, dscv);
2847 case ProcessType.HYPEREXP
2848 % Rebuilt from its own (p,lambda1,lambda2) when available: a
2849 % HyperExp carries three parameters, so a two-moment refit
2850 % returns some other HyperExp of the same mean and SCV, not
2851 % this one (HyperExp(0.1,1,10) comes back as
2852 % (0.99,6.14,0.348)).
2853 if ~isempty(dparams) && length(dparams) >= 3
2854 jdist = jline.lang.processes.HyperExp(dparams(1), dparams(2), dparams(3));
2855 else
2856 jdist = jline.lang.processes.HyperExp.fitMeanAndSCV(dmean, dscv);
2857 end
2858 case ProcessType.COXIAN
2859 jdist = jline.lang.processes.Coxian.fitMeanAndSCV(dmean, dscv);
2860 case ProcessType.APH
2861 jdist = jline.lang.processes.APH.fitMeanAndSCV(dmean, dscv);
2862 case {ProcessType.PH, ProcessType.MAP}
2863 if ~isempty(dproc)
2864 if ptype == ProcessType.PH
2865 jdist = jline.lang.processes.PH(JLINE.from_line_matrix(dproc{1}), JLINE.from_line_matrix(dproc{2}));
2866 else
2867 jdist = jline.lang.processes.MAP(JLINE.from_line_matrix(dproc{1}), JLINE.from_line_matrix(dproc{2}));
2868 end
2869 else
2870 jdist = jline.lang.processes.Exp(1/dmean);
2871 end
2872 case ProcessType.DET
2873 jdist = jline.lang.processes.Det(dmean);
2874 otherwise
2875 % Any other type is carried by mean and SCV, which is exact
2876 % for SCV 1 and a two-moment fit otherwise.
2877 if abs(dscv-1) < GlobalConstants.FineTol
2878 jdist = jline.lang.processes.Exp(1/dmean);
2879 else
2880 jdist = jline.lang.processes.APH.fitMeanAndSCV(dmean, dscv);
2881 end
2882 end
2883 end
2884
2885 function jline_matrix = from_line_matrix(matrix)
2886 [rows, cols] = size(matrix);
2887 jline_matrix = jline.util.matrix.Matrix(rows, cols);
2888 for row = 1:rows
2889 for col = 1:cols
2890 if matrix(row,col) ~= 0
2891 jline_matrix.set(row-1, col-1, matrix(row, col));
2892 end
2893 end
2894 end
2895 end
2896
2897 function lsn = from_jline_struct_layered(jlayerednetwork, jlsn)
2898 lsn = LayeredNetworkStruct();
2899 lsn.nidx= jlsn.nidx;
2900 lsn.nhosts= jlsn.nhosts;
2901 lsn.ntasks= jlsn.ntasks;
2902 lsn.nentries= jlsn.nentries;
2903 lsn.nacts= jlsn.nacts;
2904 lsn.ncalls= jlsn.ncalls;
2905 lsn.hshift= jlsn.hshift;
2906 lsn.tshift= jlsn.tshift;
2907 lsn.eshift= jlsn.eshift;
2908 lsn.ashift= jlsn.ashift;
2909 lsn.cshift= jlsn.cshift;
2910 for h=1:jlsn.nhosts
2911 lsn.tasksof{h,1} = JLINE.arraylist_to_matrix(jlsn.tasksof.get(uint32(h)))';
2912 end
2913 for t=1:jlsn.ntasks
2914 lsn.entriesof{lsn.tshift+t,1} = JLINE.arraylist_to_matrix(jlsn.entriesof.get(uint32(jlsn.tshift+t)))';
2915 end
2916 for t=1:(jlsn.ntasks+jlsn.nentries)
2917 lsn.actsof{lsn.tshift+t,1} = JLINE.arraylist_to_matrix(jlsn.actsof.get(uint32(jlsn.tshift+t)))';
2918 end
2919 for a=1:jlsn.nacts
2920 lsn.callsof{lsn.ashift+a,1} = JLINE.arraylist_to_matrix(jlsn.callsof.get(uint32(jlsn.ashift+a)))';
2921 end
2922 for i = 1:jlsn.sched.size
2923 lsn.sched(i,1) = SchedStrategy.(char(jlsn.sched.get(uint32(i))));
2924 end
2925 for i = 1:jlsn.names.size
2926 lsn.names{i,1} = jlsn.names.get(uint32(i));
2927 lsn.hashnames{i,1} = jlsn.hashnames.get(uint32(i));
2928 end
2929 lsn.mult = JLINE.from_jline_matrix(jlsn.mult);
2930 lsn.mult = lsn.mult(2:(lsn.eshift+1))'; % remove 0-padding
2931 lsn.maxmult = JLINE.from_jline_matrix(jlsn.maxmult);
2932 lsn.maxmult = lsn.maxmult(2:(lsn.eshift+1))'; % remove 0-padding
2933
2934 lsn.repl = JLINE.from_jline_matrix(jlsn.repl)';
2935 lsn.repl = lsn.repl(2:end); % remove 0-padding
2936 lsn.type = JLINE.from_jline_matrix(jlsn.type)';
2937 lsn.type = lsn.type(2:end); % remove 0-padding
2938 lsn.parent = JLINE.from_jline_matrix(jlsn.parent);
2939 lsn.parent = lsn.parent(2:end); % remove 0-padding
2940 lsn.nitems = JLINE.from_jline_matrix(jlsn.nitems);
2941 % Ensure proper column vector format matching MATLAB's (nhosts+ntasks+nentries) x 1
2942 if isrow(lsn.nitems)
2943 lsn.nitems = lsn.nitems(2:end)'; % remove 0-padding and transpose
2944 else
2945 lsn.nitems = lsn.nitems(2:end); % remove 0-padding (already column)
2946 end
2947 % Ensure correct size
2948 expectedSize = lsn.nhosts + lsn.ntasks + lsn.nentries;
2949 if length(lsn.nitems) < expectedSize
2950 lsn.nitems(expectedSize,1) = 0;
2951 elseif length(lsn.nitems) > expectedSize
2952 lsn.nitems = lsn.nitems(1:expectedSize);
2953 end
2954 lsn.replacestrat = JLINE.from_jline_matrix(jlsn.replacestrat);
2955 lsn.replacestrat = lsn.replacestrat(2:end)'; % remove 0-padding
2956 for i = 1:jlsn.callnames.size
2957 lsn.callnames{i,1} = jlsn.callnames.get(uint32(i));
2958 lsn.callhashnames{i,1} = jlsn.callhashnames.get(uint32(i));
2959 end
2960 for i = 1:jlsn.calltype.size % calltype may be made into a matrix in Java
2961 ct = char(jlsn.calltype.get(uint32(i)));
2962 lsn.calltype(i) = CallType.(ct);
2963 end
2964 lsn.calltype = sparse(lsn.calltype'); % remove 0-padding
2965 lsn.callpair = JLINE.from_jline_matrix(jlsn.callpair);
2966 lsn.callpair = lsn.callpair(2:end,2:end); % remove 0-paddings
2967 if isempty(lsn.callpair)
2968 lsn.callpair=[];
2969 end
2970 lsn.actpretype = sparse(JLINE.from_jline_matrix(jlsn.actpretype)');
2971 lsn.actpretype = lsn.actpretype(2:end); % remove 0-padding
2972 lsn.actposttype = sparse(JLINE.from_jline_matrix(jlsn.actposttype)');
2973 lsn.actposttype = lsn.actposttype(2:end); % remove 0-padding
2974 lsn.graph = JLINE.from_jline_matrix(jlsn.graph);
2975 lsn.graph = lsn.graph(2:end,2:end); % remove 0-paddings
2976 lsn.dag = JLINE.from_jline_matrix(jlsn.dag);
2977 lsn.dag = lsn.dag(2:end,2:end); % remove 0-paddings
2978 lsn.taskgraph = JLINE.from_jline_matrix(jlsn.taskgraph);
2979 lsn.taskgraph = sparse(lsn.taskgraph(2:end,2:end)); % remove 0-paddings
2980 lsn.replygraph = JLINE.from_jline_matrix(jlsn.replygraph);
2981 lsn.replygraph = logical(lsn.replygraph(2:end,2:end)); % remove 0-paddings
2982 lsn.iscache = JLINE.from_jline_matrix(jlsn.iscache);
2983 % Ensure proper column vector format matching MATLAB's (nhosts+ntasks) x 1
2984 expectedCacheSize = lsn.nhosts + lsn.ntasks;
2985 if isrow(lsn.iscache)
2986 if length(lsn.iscache) > expectedCacheSize
2987 lsn.iscache = lsn.iscache(2:(expectedCacheSize+1))'; % remove 0-padding and transpose
2988 else
2989 lsn.iscache = lsn.iscache'; % just transpose
2990 end
2991 end
2992 % Ensure correct size
2993 if length(lsn.iscache) < expectedCacheSize
2994 lsn.iscache(expectedCacheSize,1) = 0;
2995 elseif length(lsn.iscache) > expectedCacheSize
2996 lsn.iscache = lsn.iscache(1:expectedCacheSize);
2997 end
2998 lsn.iscaller = JLINE.from_jline_matrix(jlsn.iscaller);
2999 lsn.iscaller = full(lsn.iscaller(2:end,2:end)); % remove 0-paddings
3000 lsn.issynccaller = JLINE.from_jline_matrix(jlsn.issynccaller);
3001 lsn.issynccaller = full(lsn.issynccaller(2:end,2:end)); % remove 0-paddings
3002 lsn.isasynccaller = JLINE.from_jline_matrix(jlsn.isasynccaller);
3003 lsn.isasynccaller = full(lsn.isasynccaller(2:end,2:end)); % remove 0-paddings
3004 lsn.isref = JLINE.from_jline_matrix(jlsn.isref);
3005 lsn.isref = lsn.isref(2:end)'; % remove 0-paddings
3006 end
3007
3008 function sn = from_jline_struct(jnetwork, jsn)
3009 %lst and rtfun are not implemented
3010 %Due to the transformation of Java lambda to matlab function
3011 if nargin<2
3012 jsn = jnetwork.getStruct(false);
3013 end
3014 jclasses = jnetwork.getClasses();
3015 jnodes = jnetwork.getNodes();
3016 jstateful = jnetwork.getStatefulNodes();
3017 jstations = jnetwork.getStations();
3018 sn = NetworkStruct();
3019
3020 sn.nnodes = jsn.nnodes;
3021 sn.nclasses = jsn.nclasses;
3022 sn.nclosedjobs = jsn.nclosedjobs;
3023 sn.nstations = jsn.nstations;
3024 sn.nstateful = jsn.nstateful;
3025 sn.nchains = jsn.nchains;
3026
3027 sn.refstat = JLINE.from_jline_matrix(jsn.refstat) + 1;
3028 sn.njobs = JLINE.from_jline_matrix(jsn.njobs);
3029 sn.nservers = JLINE.from_jline_matrix(jsn.nservers);
3030 sn.connmatrix = JLINE.from_jline_matrix(jsn.connmatrix);
3031 % Fix for Java getConnectionMatrix bug: ensure connmatrix is nnodes x nnodes
3032 if size(sn.connmatrix,1) < sn.nnodes
3033 sn.connmatrix(sn.nnodes,1) = 0;
3034 end
3035 if size(sn.connmatrix,2) < sn.nnodes
3036 sn.connmatrix(1,sn.nnodes) = 0;
3037 end
3038 sn.scv = JLINE.from_jline_matrix(jsn.scv);
3039 sn.isstation = logical(JLINE.from_jline_matrix(jsn.isstation));
3040 sn.isstateful = logical(JLINE.from_jline_matrix(jsn.isstateful));
3041 sn.isstatedep = logical(JLINE.from_jline_matrix(jsn.isstatedep));
3042 sn.nodeToStateful = JLINE.from_jline_matrix(jsn.nodeToStateful)+1;
3043 sn.nodeToStateful(sn.nodeToStateful==0) = nan;
3044 sn.nodeToStation = JLINE.from_jline_matrix(jsn.nodeToStation)+1;
3045 sn.nodeToStation(sn.nodeToStation==0) = nan;
3046 sn.stationToNode = JLINE.from_jline_matrix(jsn.stationToNode)+1;
3047 sn.stationToNode(sn.stationToNode==0) = nan;
3048 sn.stationToStateful = JLINE.from_jline_matrix(jsn.stationToStateful)+1;
3049 sn.stationToStateful(sn.stationToStateful==0) = nan;
3050 sn.statefulToStation = JLINE.from_jline_matrix(jsn.statefulToStation)+1;
3051 sn.statefulToStation(sn.statefulToStation==0) = nan;
3052 sn.statefulToNode = JLINE.from_jline_matrix(jsn.statefulToNode)+1;
3053 sn.statefulToNode(sn.statefulToNode==0) = nan;
3054 sn.rates = JLINE.from_jline_matrix(jsn.rates);
3055 sn.fj = JLINE.from_jline_matrix(jsn.fj);
3056 sn.classprio = JLINE.from_jline_matrix(jsn.classprio);
3057 sn.phases = JLINE.from_jline_matrix(jsn.phases);
3058 sn.phasessz = JLINE.from_jline_matrix(jsn.phasessz);
3059 sn.phaseshift = JLINE.from_jline_matrix(jsn.phaseshift);
3060 sn.schedparam = JLINE.from_jline_matrix(jsn.schedparam);
3061 sn.chains = logical(JLINE.from_jline_matrix(jsn.chains));
3062 sn.rt = JLINE.from_jline_matrix(jsn.rt);
3063 sn.nvars = JLINE.from_jline_matrix(jsn.nvars);
3064 sn.rtnodes = JLINE.from_jline_matrix(jsn.rtnodes);
3065 sn.csmask = logical(JLINE.from_jline_matrix(jsn.csmask));
3066 sn.isslc = logical(JLINE.from_jline_matrix(jsn.isslc));
3067 sn.cap = JLINE.from_jline_matrix(jsn.cap);
3068 sn.classcap = JLINE.from_jline_matrix(jsn.classcap);
3069 sn.refclass = JLINE.from_jline_matrix(jsn.refclass)+1;
3070 sn.lldscaling = JLINE.from_jline_matrix(jsn.lldscaling);
3071
3072 if ~isempty(jsn.cdscaling) && jsn.cdscaling.size() > 0
3073 % Convert Java SerializableFunction to MATLAB function handles
3074 sn.cdscaling = cell(sn.nstations, 1);
3075 % Iterate through the map entries to handle null values properly
3076 entrySet = jsn.cdscaling.entrySet();
3077 entryIter = entrySet.iterator();
3078 stationFunMap = containers.Map();
3079 while entryIter.hasNext()
3080 entry = entryIter.next();
3081 stationName = char(entry.getKey().getName());
3082 try
3083 jfun = entry.getValue();
3084 if ~isempty(jfun)
3085 stationFunMap(stationName) = jfun;
3086 end
3087 catch
3088 % getValue() returns null for default lambda functions
3089 % Skip and use default value
3090 end
3091 end
3092 % Assign functions to stations
3093 for i = 1:sn.nstations
3094 jstation = jstations.get(i-1);
3095 stationName = char(jstation.getName());
3096 if isKey(stationFunMap, stationName)
3097 jfun = stationFunMap(stationName);
3098 % Create a MATLAB function handle that calls the Java apply() method
3099 sn.cdscaling{i} = @(ni) JLINE.call_java_cdscaling(jfun, ni);
3100 else
3101 sn.cdscaling{i} = @(ni) 1;
3102 end
3103 end
3104 else
3105 sn.cdscaling = cell(sn.nstations, 0);
3106 end
3107
3108 if ~isempty(jsn.nodetype)
3109 sn.nodetype = zeros(sn.nnodes, 1);
3110 for i = 1:jsn.nodetype.size
3111 nodetype = jsn.nodetype.get(i-1);
3112 switch nodetype.name().toCharArray'
3113 case 'Queue'
3114 sn.nodetype(i) = NodeType.Queue;
3115 case 'Delay'
3116 sn.nodetype(i) = NodeType.Delay;
3117 case 'Source'
3118 sn.nodetype(i) = NodeType.Source;
3119 case 'Sink'
3120 sn.nodetype(i) = NodeType.Sink;
3121 case 'Join'
3122 sn.nodetype(i) = NodeType.Join;
3123 case 'Fork'
3124 sn.nodetype(i) = NodeType.Fork;
3125 case 'ClassSwitch'
3126 sn.nodetype(i) = NodeType.ClassSwitch;
3127 case 'Logger'
3128 sn.nodetype(i) = NodeType.Logger;
3129 case 'Cache'
3130 sn.nodetype(i) = NodeType.Cache;
3131 case 'Place'
3132 sn.nodetype(i) = NodeType.Place;
3133 case 'Transition'
3134 sn.nodetype(i) = NodeType.Transition;
3135 case 'Router'
3136 sn.nodetype(i) = NodeType.Router;
3137 end
3138 end
3139 else
3140 sn.nodetype = [];
3141 end
3142
3143 if ~isempty(jsn.classnames)
3144 for i = 1:jsn.classnames.size
3145 sn.classnames(i,1) = jsn.classnames.get(i-1);
3146 end
3147 else
3148 sn.classnames = [];
3149 end
3150
3151 if ~isempty(jsn.nodenames)
3152 for i = 1:jsn.nodenames.size
3153 sn.nodenames(i,1) = jsn.nodenames.get(i-1);
3154 end
3155 else
3156 sn.nodenames = [];
3157 end
3158
3159 if ~isempty(jsn.rtorig) && jsn.rtorig.size()>0
3160 sn.rtorig = cell(sn.nclasses, sn.nclasses);
3161 for r = 1:sn.nclasses
3162 for s = 1:sn.nclasses
3163 sn.rtorig{r,s} = JLINE.from_jline_matrix(jsn.rtorig.get(jclasses.get(r-1)).get(jclasses.get(s-1)));
3164 end
3165 end
3166 else
3167 sn.rtorig = {};
3168 end
3169
3170 if ~isempty(jsn.state)
3171 sn.state = cell(sn.nstateful, 1);
3172 for i = 1:sn.nstateful
3173 sn.state{i} = JLINE.from_jline_matrix(jstateful.get(i-1).getState());
3174 end
3175 else
3176 sn.state = {};
3177 end
3178
3179 if ~isempty(jsn.stateprior)
3180 sn.stateprior = cell(sn.nstateful, 1);
3181 for i = 1:sn.nstateful
3182 sn.stateprior{i} = JLINE.from_jline_matrix(jstateful.get(i-1).getStatePrior());
3183 end
3184 else
3185 sn.stateprior = {};
3186 end
3187
3188 if ~isempty(jsn.space)
3189 sn.space = cell(sn.nstateful, 1);
3190 for i = 1:sn.nstateful
3191 sn.space{i} = JLINE.from_jline_matrix(jstateful.get(i-1).getStateSpace());
3192 end
3193 else
3194 sn.space = {};
3195 end
3196
3197 if ~isempty(jsn.routing)
3198 sn.routing = zeros(sn.nnodes, sn.nclasses);
3199 for i = 1:sn.nnodes
3200 for j = 1:sn.nclasses
3201 routingStrategy = jsn.routing.get(jnodes.get(i-1)).get(jclasses.get(j-1));
3202 switch routingStrategy.name().toCharArray'
3203 case 'PROB'
3204 sn.routing(i,j) = RoutingStrategy.PROB;
3205 case 'RAND'
3206 sn.routing(i,j) = RoutingStrategy.RAND;
3207 case 'RROBIN'
3208 sn.routing(i,j) = RoutingStrategy.RROBIN;
3209 case 'WRROBIN'
3210 sn.routing(i,j) = RoutingStrategy.WRROBIN;
3211 case 'JSQ'
3212 sn.routing(i,j) = RoutingStrategy.JSQ;
3213 case 'DISABLED'
3214 sn.routing(i,j) = RoutingStrategy.DISABLED;
3215 case 'FIRING'
3216 sn.routing(i,j) = RoutingStrategy.FIRING;
3217 case 'KCHOICES'
3218 sn.routing(i,j) = RoutingStrategy.KCHOICES;
3219 end
3220 end
3221 end
3222 else
3223 sn.routing = [];
3224 end
3225
3226 if ~isempty(jsn.procid)
3227 sn.procid = nan(sn.nstations, sn.nclasses); % Initialize with NaN to match MATLAB behavior
3228 for i = 1:sn.nstations
3229 for j = 1:sn.nclasses
3230 stationMap = jsn.procid.get(jstations.get(i-1));
3231 if isempty(stationMap)
3232 sn.procid(i,j) = ProcessType.DISABLED;
3233 continue;
3234 end
3235 processType = stationMap.get(jclasses.get(j-1));
3236 if isempty(processType)
3237 sn.procid(i,j) = ProcessType.DISABLED;
3238 continue;
3239 end
3240 switch processType.name.toCharArray'
3241 case 'EXP'
3242 sn.procid(i,j) = ProcessType.EXP;
3243 case 'ERLANG'
3244 sn.procid(i,j) = ProcessType.ERLANG;
3245 case 'HYPEREXP'
3246 sn.procid(i,j) = ProcessType.HYPEREXP;
3247 case 'PH'
3248 sn.procid(i,j) = ProcessType.PH;
3249 case 'APH'
3250 sn.procid(i,j) = ProcessType.APH;
3251 case 'MAP'
3252 sn.procid(i,j) = ProcessType.MAP;
3253 case 'UNIFORM'
3254 sn.procid(i,j) = ProcessType.UNIFORM;
3255 case 'DET'
3256 sn.procid(i,j) = ProcessType.DET;
3257 case 'COXIAN'
3258 sn.procid(i,j) = ProcessType.COXIAN;
3259 case 'GAMMA'
3260 sn.procid(i,j) = ProcessType.GAMMA;
3261 case 'PARETO'
3262 sn.procid(i,j) = ProcessType.PARETO;
3263 case 'WEIBULL'
3264 sn.procid(i,j) = ProcessType.WEIBULL;
3265 case 'LOGNORMAL'
3266 sn.procid(i,j) = ProcessType.LOGNORMAL;
3267 case 'MMPP2'
3268 sn.procid(i,j) = ProcessType.MMPP2;
3269 case 'REPLAYER'
3270 sn.procid(i,j) = ProcessType.REPLAYER;
3271 case 'TRACE'
3272 sn.procid(i,j) = ProcessType.TRACE;
3273 case 'IMMEDIATE'
3274 sn.procid(i,j) = ProcessType.IMMEDIATE;
3275 case 'DISABLED'
3276 sn.procid(i,j) = ProcessType.DISABLED;
3277 case 'COX2'
3278 sn.procid(i,j) = ProcessType.COX2;
3279 case 'BMAP'
3280 sn.procid(i,j) = ProcessType.BMAP;
3281 case 'ME'
3282 sn.procid(i,j) = ProcessType.ME;
3283 case 'RAP'
3284 sn.procid(i,j) = ProcessType.RAP;
3285 case 'BINOMIAL'
3286 sn.procid(i,j) = ProcessType.BINOMIAL;
3287 case 'POISSON'
3288 sn.procid(i,j) = ProcessType.POISSON;
3289 case 'GEOMETRIC'
3290 sn.procid(i,j) = ProcessType.GEOMETRIC;
3291 case 'DUNIFORM'
3292 sn.procid(i,j) = ProcessType.DUNIFORM;
3293 case 'BERNOULLI'
3294 sn.procid(i,j) = ProcessType.BERNOULLI;
3295 case 'PRIOR'
3296 sn.procid(i,j) = ProcessType.PRIOR;
3297 otherwise
3298 % Unknown ProcessType - default to DISABLED
3299 sn.procid(i,j) = ProcessType.DISABLED;
3300 end
3301 end
3302 end
3303 else
3304 sn.procid = [];
3305 end
3306
3307 if ~isempty(jsn.mu)
3308 sn.mu = cell(sn.nstations, 1);
3309 for i = 1:sn.nstations
3310 sn.mu{i} = cell(1, sn.nclasses);
3311 for j = 1:sn.nclasses
3312 sn.mu{i}{j} = JLINE.from_jline_matrix(jsn.mu.get(jstations.get(i-1)).get(jclasses.get(j-1)));
3313 end
3314 end
3315 else
3316 sn.mu = {};
3317 end
3318
3319 if ~isempty(jsn.phi)
3320 sn.phi = cell(sn.nstations, 1);
3321 for i = 1:sn.nstations
3322 sn.phi{i} = cell(1, sn.nclasses);
3323 for j = 1:sn.nclasses
3324 sn.phi{i}{j} = JLINE.from_jline_matrix(jsn.phi.get(jstations.get(i-1)).get(jclasses.get(j-1)));
3325 end
3326 end
3327 else
3328 sn.phi = {};
3329 end
3330
3331 if ~isempty(jsn.proc)
3332 sn.proc = cell(sn.nstations, 1);
3333 for i = 1:sn.nstations
3334 sn.proc{i} = cell(1, sn.nclasses);
3335 for j = 1:sn.nclasses
3336 proc_i_j = jsn.proc.get(jstations.get(i-1)).get(jclasses.get(j-1));
3337 sn.proc{i}{j} = cell(1, proc_i_j.size);
3338 for k = 1:proc_i_j.size
3339 sn.proc{i}{j}{k} = JLINE.from_jline_matrix(proc_i_j.get(uint32(k-1)));
3340 end
3341 end
3342 end
3343 else
3344 sn.proc = {};
3345 end
3346
3347 if ~isempty(jsn.pie)
3348 sn.pie = cell(sn.nstations, 1);
3349 for i = 1:sn.nstations
3350 sn.pie{i} = cell(1, sn.nclasses);
3351 for j = 1:sn.nclasses
3352 sn.pie{i}{j} = JLINE.from_jline_matrix(jsn.pie.get(jstations.get(i-1)).get(jclasses.get(j-1)));
3353 end
3354 end
3355 else
3356 sn.pie = {};
3357 end
3358
3359 if ~isempty(jsn.sched)
3360 sn.sched = zeros(sn.nstations, 1);
3361 for i = 1:sn.nstations
3362 schedStrategy = jsn.sched.get(jstations.get(i-1));
3363 switch schedStrategy.name.toCharArray'
3364 case 'INF'
3365 sn.sched(i) = SchedStrategy.INF;
3366 case 'FCFS'
3367 sn.sched(i) = SchedStrategy.FCFS;
3368 case 'LCFS'
3369 sn.sched(i) = SchedStrategy.LCFS;
3370 case 'LCFSPR'
3371 sn.sched(i) = SchedStrategy.LCFSPR;
3372 case 'SIRO'
3373 sn.sched(i) = SchedStrategy.SIRO;
3374 case 'SJF'
3375 sn.sched(i) = SchedStrategy.SJF;
3376 case 'LJF'
3377 sn.sched(i) = SchedStrategy.LJF;
3378 case 'PS'
3379 sn.sched(i) = SchedStrategy.PS;
3380 case 'DPS'
3381 sn.sched(i) = SchedStrategy.DPS;
3382 case 'GPS'
3383 sn.sched(i) = SchedStrategy.GPS;
3384 case 'PSPRIO'
3385 sn.sched(i) = SchedStrategy.PSPRIO;
3386 case 'DPSPRIO'
3387 sn.sched(i) = SchedStrategy.DPSPRIO;
3388 case 'GPSPRIO'
3389 sn.sched(i) = SchedStrategy.GPSPRIO;
3390 case 'SEPT'
3391 sn.sched(i) = SchedStrategy.SEPT;
3392 case 'LEPT'
3393 sn.sched(i) = SchedStrategy.LEPT;
3394 case {'HOL', 'FCFSPRIO'}
3395 sn.sched(i) = SchedStrategy.FCFSPRIO;
3396 case 'FORK'
3397 sn.sched(i) = SchedStrategy.FORK;
3398 case 'EXT'
3399 sn.sched(i) = SchedStrategy.EXT;
3400 case 'REF'
3401 sn.sched(i) = SchedStrategy.REF;
3402 end
3403 end
3404 else
3405 sn.sched = [];
3406 end
3407
3408 if ~isempty(jsn.inchain)
3409 sn.inchain = cell(1, sn.nchains);
3410 for i = 1:sn.nchains
3411 sn.inchain{1,i} = JLINE.from_jline_matrix(jsn.inchain.get(uint32(i-1)))+1;
3412 end
3413 else
3414 sn.inchain = {};
3415 end
3416
3417 if ~isempty(jsn.visits)
3418 sn.visits = cell(sn.nchains, 1);
3419 for i = 1:sn.nchains
3420 sn.visits{i,1} = JLINE.from_jline_matrix(jsn.visits.get(uint32(i-1)));
3421 end
3422 else
3423 sn.visits = {};
3424 end
3425
3426 if ~isempty(jsn.nodevisits)
3427 sn.nodevisits = cell(1, sn.nchains);
3428 for i = 1:sn.nchains
3429 sn.nodevisits{1,i} = JLINE.from_jline_matrix(jsn.nodevisits.get(uint32(i-1)));
3430 end
3431 else
3432 sn.nodevisits = {};
3433 end
3434
3435 if ~isempty(jsn.droprule)
3436 sn.droprule = zeros(sn.nstations, sn.nclasses);
3437 for i = 1:sn.nstations
3438 for j = 1:sn.nclasses
3439 dropStrategy = jsn.droprule.get(jstations.get(i-1)).get(jclasses.get(j-1));
3440 switch dropStrategy.name.toCharArray'
3441 case 'WaitingQueue'
3442 sn.droprule(i,j) = DropStrategy.WAITQ;
3443 case 'Drop'
3444 sn.droprule(i,j) = DropStrategy.DROP;
3445 case 'BlockingAfterService'
3446 sn.droprule(i,j) = DropStrategy.BAS;
3447 end
3448 end
3449 end
3450 else
3451 sn.droprule = [];
3452 end
3453
3454 if ~isempty(jsn.nodeparam)
3455 sn.nodeparam = cell(sn.nnodes, 1);
3456
3457 for i = 1:sn.nnodes
3458 jnode = jnodes.get(i-1);
3459 jparam = jsn.nodeparam.get(jnode);
3460
3461 %if jparam.isEmpty
3462 % sn.nodeparam{i} = [];
3463 % continue;
3464 %end
3465
3466 % StationNodeParam
3467 if isa(jparam, 'jline.lang.nodeparam.StationNodeParam')
3468 if ~isempty(jparam.fileName)
3469 sn.nodeparam{i}.fileName = cell(1, sn.nclasses);
3470 for r = 1:sn.nclasses
3471 fname = jparam.fileName.get(r-1);
3472 if ~isempty(fname)
3473 sn.nodeparam{i}.fileName{r} = char(fname);
3474 end
3475 end
3476 end
3477 end
3478
3479 % TransitionNodeParam
3480 if isa(jparam, 'jline.lang.nodeparam.TransitionNodeParam')
3481 if ~isempty(jparam.firingprocid)
3482 sn.nodeparam{i}.firingprocid = containers.Map('KeyType', 'char', 'ValueType', 'any');
3483 keys = jparam.firingprocid.keySet.iterator;
3484 while keys.hasNext
3485 key = keys.next;
3486 proc = jparam.firingprocid.get(key);
3487 sn.nodeparam{i}.firingprocid(char(key.toString)) = char(proc.toString);
3488 end
3489 end
3490 if ~isempty(jparam.firingphases)
3491 sn.nodeparam{i}.firingphases = JLINE.from_jline_matrix(jparam.firingphases);
3492 end
3493 if ~isempty(jparam.fireweight)
3494 sn.nodeparam{i}.fireweight = JLINE.from_jline_matrix(jparam.fireweight);
3495 end
3496 end
3497
3498 % JoinNodeParam
3499 if isa(jparam, 'jline.lang.nodeparam.JoinNodeParam')
3500 if ~isempty(jparam.joinStrategy)
3501 sn.nodeparam{i}.joinStrategy = cell(1, sn.nclasses);
3502 sn.nodeparam{i}.fanIn = cell(1, sn.nclasses);
3503 for r = 1:sn.nclasses
3504 jclass = jclasses.get(r-1);
3505 joinStrategy = jparam.joinStrategy.get(jclass);
3506 if ~isempty(joinStrategy)
3507 strategyStr = char(joinStrategy.name.toString);
3508 switch strategyStr
3509 case 'STD'
3510 sn.nodeparam{i}.joinStrategy{r} = JoinStrategy.STD;
3511 case 'PARTIAL'
3512 sn.nodeparam{i}.joinStrategy{r} = JoinStrategy.PARTIAL;
3513 otherwise
3514 sn.nodeparam{i}.joinStrategy{r} = strategyStr;
3515 end
3516 sn.nodeparam{i}.fanIn{r} = jparam.fanIn.get(jclass);
3517 end
3518 end
3519 end
3520 end
3521
3522 % RoutingNodeParam
3523 if isa(jparam, 'jline.lang.nodeparam.RoutingNodeParam')
3524 for r = 1:sn.nclasses
3525 jclass = jclasses.get(r-1);
3526
3527 if ~isempty(jparam.weights) && jparam.weights.containsKey(jclass)
3528 sn.nodeparam{i}.weights{r} = JLINE.from_jline_matrix(jparam.weights.get(jclass));
3529 end
3530
3531 if ~isempty(jparam.outlinks) && jparam.outlinks.containsKey(jclass)
3532 sn.nodeparam{i}.outlinks{r} = JLINE.from_jline_matrix(jparam.outlinks.get(jclass));
3533 end
3534 end
3535 end
3536
3537 % ForkNodeParam
3538 if isa(jparam, 'jline.lang.nodeparam.ForkNodeParam')
3539 if ~isnan(jparam.fanOut)
3540 sn.nodeparam{i}.fanOut = jparam.fanOut;
3541 end
3542 end
3543
3544 % CacheNodeParam
3545 if isa(jparam, 'jline.lang.nodeparam.CacheNodeParam')
3546 % nitems
3547 if ~isnan(jparam.nitems)
3548 sn.nodeparam{i}.nitems = jparam.nitems;
3549 end
3550
3551 % accost
3552 if ~isempty(jparam.accost)
3553 % For Java 2D arrays (Matrix[][]), size(arr,2) returns 1 in MATLAB
3554 % We need to get length of first row to get actual second dimension
3555 K1 = size(jparam.accost, 1);
3556 if K1 > 0
3557 firstRow = jparam.accost(1); % Get first row (Java array)
3558 K2 = length(firstRow);
3559 else
3560 K2 = 0;
3561 end
3562 sn.nodeparam{i}.accost = cell(K1, K2);
3563 for k1 = 1:K1
3564 for k2 = 1:K2
3565 mat = jparam.accost(k1, k2); % MATLAB handles Java array indexing
3566 if ~isempty(mat)
3567 sn.nodeparam{i}.accost{k1, k2} = JLINE.from_jline_matrix(mat);
3568 end
3569 end
3570 end
3571 end
3572
3573 % itemcap
3574 if ~isempty(jparam.itemcap)
3575 sn.nodeparam{i}.itemcap = JLINE.from_jline_matrix(jparam.itemcap);
3576 end
3577
3578 % pread - convert from Java Map<Integer, List<Double>> to MATLAB cell array {R}
3579 if ~isempty(jparam.pread)
3580 nclasses = sn.nclasses;
3581 sn.nodeparam{i}.pread = cell(1, nclasses);
3582 for r = 1:nclasses
3583 list = jparam.pread.get(int32(r-1)); % Java 0-based indexing
3584 if ~isempty(list)
3585 values = zeros(1, list.size);
3586 for j = 1:list.size
3587 values(j) = list.get(j-1);
3588 end
3589 sn.nodeparam{i}.pread{r} = values;
3590 else
3591 sn.nodeparam{i}.pread{r} = NaN;
3592 end
3593 end
3594 end
3595
3596 % replacestrat
3597 if ~isempty(jparam.replacestrat)
3598 switch char(jparam.replacestrat)
3599 case 'RR'
3600 sn.nodeparam{i}.replacestrat = ReplacementStrategy.RR;
3601 case 'FIFO'
3602 sn.nodeparam{i}.replacestrat = ReplacementStrategy.FIFO;
3603 case 'SFIFO'
3604 sn.nodeparam{i}.replacestrat = ReplacementStrategy.SFIFO;
3605 case 'LRU'
3606 sn.nodeparam{i}.replacestrat = ReplacementStrategy.LRU;
3607 end
3608 end
3609
3610 % hitclass
3611 if ~isempty(jparam.hitclass)
3612 sn.nodeparam{i}.hitclass = 1+JLINE.from_jline_matrix(jparam.hitclass);
3613 end
3614
3615 % missclass
3616 if ~isempty(jparam.missclass)
3617 sn.nodeparam{i}.missclass =1+ JLINE.from_jline_matrix(jparam.missclass);
3618 end
3619
3620 % actual hit/miss probabilities
3621 if ~isempty(jparam.actualhitprob)
3622 sn.nodeparam{i}.actualhitprob = JLINE.from_jline_matrix(jparam.actualhitprob);
3623 end
3624 if ~isempty(jparam.actualmissprob)
3625 sn.nodeparam{i}.actualmissprob = JLINE.from_jline_matrix(jparam.actualmissprob);
3626 end
3627 end
3628 end
3629 else
3630 sn.nodeparam = {};
3631 end
3632
3633 % if ~isempty(jsn.nodeparam)
3634 % sn.nodeparam = cell(sn.nnodes, 1);
3635 % % Note that JLINE only support node parameters related to
3636 % % Fork, Join, WWROBIN and RROBIN
3637 % for i = 1:sn.nnodes
3638 % if jsn.nodeparam.get(jnodes.get(i-1)).isEmpty
3639 % sn.nodeparam{i} = [];
3640 % else
3641 % if ~isnan(jsn.nodeparam.get(jnodes.get(i-1)).nitems)
3642 % sn.nodeparam{i}.nitems = jsn.nodeparam.get(jnodes.get(i-1)).nitems;
3643 % end
3644 % if ~isnan(jsn.nodeparam.get(jnodes.get(i-1)).fanOut)
3645 % sn.nodeparam{i}.fanOut = jsn.nodeparam.get(jnodes.get(i-1)).fanOut;
3646 % end
3647 % if ~isempty(jsn.nodeparam.get(jnodes.get(i-1)).joinStrategy)
3648 % if ~jsn.nodeparam.get(jnodes.get(i-1)).joinStrategy.isEmpty
3649 % sn.nodeparam{i}.joinStrategy = cell(1, sn.nclasses);
3650 % sn.nodeparam{i}.fanIn = cell(1, sn.nclasses);
3651 % for r = 1:sn.nclasses
3652 % joinStrategy = jsn.nodeparam.get(jnodes.get(i-1)).joinStrategy.get(jclasses.get(r-1));
3653 % switch joinStrategy.name.toCharArray'
3654 % case 'STD'
3655 % sn.nodeparam{i}.joinStrategy{r} = JoinStrategy.STD;
3656 % case 'PARTIAL'
3657 % sn.nodeparam{i}.joinStrategy{r} = JoinStrategy.PARTIAL;
3658 % end
3659 % sn.nodeparam{i}.fanIn{r} = jsn.nodeparam.get(jnodes.get(i-1)).fanIn.get(jclasses.get(r-1));
3660 % end
3661 % end
3662 % end
3663 %
3664 % if ~isempty(jsn.nodeparam.get(jnodes.get(i-1)).weights)
3665 % for r = 1:sn.nclasses
3666 % sn.nodeparam{i}{r}.weights = JLINE.from_jline_matrix(jsn.nodeparam.get(jnodes.get(i-1)).weights.get(jclasses.get(r-1)));
3667 % end
3668 % end
3669 %
3670 % if ~isempty(jsn.nodeparam.get(jnodes.get(i-1)).outlinks)
3671 % for r = 1:sn.nclasses
3672 % sn.nodeparam{i}{r}.outlinks = JLINE.from_jline_matrix(jsn.nodeparam.get(jnodes.get(i-1)).outlinks.get(jclasses.get(r-1)));
3673 % end
3674 % end
3675 % end
3676 % end
3677 % else
3678 % sn.nodeparam = {};
3679 % end
3680
3681 if ~isempty(jsn.sync)
3682 jsync = jsn.sync;
3683 sn.sync = cell(jsync.size, 1);
3684 for i = 1:jsync.size
3685 jsync_i = jsync.get(uint32(i-1));
3686 sn.sync{i,1} = struct('active',cell(1),'passive',cell(1));
3687
3688 jactive = jsync_i.active.get(uint32(0));
3689 jpassive = jsync_i.passive.get(uint32(0));
3690
3691 %Currently assume that prob would always be a value
3692 %instead of lambda function (No idea of how to convert
3693 %Java lambda function to matlab lambda function)
3694 switch jactive.getEvent.name.toCharArray'
3695 case 'INIT'
3696 sn.sync{i,1}.active{1} = Event(EventType.INIT, jactive.getNode+1, jactive.getJobClass+1, ...
3697 jactive.getProb, JLINE.from_jline_matrix(jactive.getState), ...
3698 jactive.getT, jactive.getJob);
3699 case 'LOCAL'
3700 sn.sync{i,1}.active{1} = Event(EventType.LOCAL, jactive.getNode+1, jactive.getJobClass+1, ...
3701 jactive.getProb, JLINE.from_jline_matrix(jactive.getState), ...
3702 jactive.getT, jactive.getJob);
3703 case 'ARV'
3704 sn.sync{i,1}.active{1} = Event(EventType.ARV, jactive.getNode+1, jactive.getJobClass+1, ...
3705 jactive.getProb, JLINE.from_jline_matrix(jactive.getState), ...
3706 jactive.getT, jactive.getJob);
3707 case 'DEP'
3708 sn.sync{i,1}.active{1} = Event(EventType.DEP, jactive.getNode+1, jactive.getJobClass+1, ...
3709 jactive.getProb, JLINE.from_jline_matrix(jactive.getState), ...
3710 jactive.getT, jactive.getJob);
3711 case 'PHASE'
3712 sn.sync{i,1}.active{1} = Event(EventType.PHASE, jactive.getNode+1, jactive.getJobClass+1, ...
3713 jactive.getProb, JLINE.from_jline_matrix(jactive.getState), ...
3714 jactive.getT, jactive.getJob);
3715 case 'READ'
3716 sn.sync{i,1}.active{1} = Event(EventType.READ, jactive.getNode+1, jactive.getJobClass+1, ...
3717 jactive.getProb, JLINE.from_jline_matrix(jactive.getState), ...
3718 jactive.getT, jactive.getJob);
3719 case 'STAGE'
3720 sn.sync{i,1}.active{1} = Event(EventType.STAGE, jactive.getNode+1, jactive.getJobClass+1, ...
3721 jactive.getProb, JLINE.from_jline_matrix(jactive.getState), ...
3722 jactive.getT, jactive.getJob);
3723 end
3724
3725 switch jpassive.getEvent.name.toCharArray'
3726 case 'INIT'
3727 sn.sync{i,1}.passive{1} = Event(EventType.INIT, jpassive.getNode+1, jpassive.getJobClass+1, ...
3728 jpassive.getProb, JLINE.from_jline_matrix(jpassive.getState), ...
3729 jpassive.getT, jpassive.getJob);
3730 case 'LOCAL'
3731 sn.sync{i,1}.passive{1} = Event(EventType.LOCAL, jpassive.getNode+1, jpassive.getJobClass+1, ...
3732 jpassive.getProb, JLINE.from_jline_matrix(jpassive.getState), ...
3733 jpassive.getT, jpassive.getJob);
3734 case 'ARV'
3735 sn.sync{i,1}.passive{1} = Event(EventType.ARV, jpassive.getNode+1, jpassive.getJobClass+1, ...
3736 jpassive.getProb, JLINE.from_jline_matrix(jpassive.getState), ...
3737 jpassive.getT, jpassive.getJob);
3738 case 'DEP'
3739 sn.sync{i,1}.passive{1} = Event(EventType.DEP, jpassive.getNode+1, jpassive.getJobClass+1, ...
3740 jpassive.getProb, JLINE.from_jline_matrix(jpassive.getState), ...
3741 jpassive.getT, jpassive.getJob);
3742 case 'PHASE'
3743 sn.sync{i,1}.passive{1} = Event(EventType.PHASE, jpassive.getNode+1, jpassive.getJobClass+1, ...
3744 jpassive.getProb, JLINE.from_jline_matrix(jpassive.getState), ...
3745 jpassive.getT, jpassive.getJob);
3746 case 'READ'
3747 sn.sync{i,1}.passive{1} = Event(EventType.READ, jpassive.getNode+1, jpassive.getJobClass+1, ...
3748 jpassive.getProb, JLINE.from_jline_matrix(jpassive.getState), ...
3749 jpassive.getT, jpassive.getJob);
3750 case 'STAGE'
3751 sn.sync{i,1}.passive{1} = Event(EventType.STAGE, jpassive.getNode+1, jpassive.getJobClass+1, ...
3752 jpassive.getProb, JLINE.from_jline_matrix(jpassive.getState), ...
3753 jpassive.getT, jpassive.getJob);
3754 end
3755 end
3756 else
3757 sn.sync = {};
3758 end
3759 end
3760
3761 function [QN,UN,RN,WN,AN,TN] = arrayListToResults(alist)
3762 switch class(alist)
3763 case 'jline.solvers.LayeredNetworkAvgTable'
3764 QN = JLINE.arraylist_to_matrix(alist.getQLen());
3765 UN = JLINE.arraylist_to_matrix(alist.getUtil());
3766 RN = JLINE.arraylist_to_matrix(alist.getRespT());
3767 WN = JLINE.arraylist_to_matrix(alist.getResidT());
3768 AN = JLINE.arraylist_to_matrix(alist.getArvR());
3769 TN = JLINE.arraylist_to_matrix(alist.getTput());
3770 otherwise
3771 QN = JLINE.arraylist_to_matrix(alist.getQLen());
3772 UN = JLINE.arraylist_to_matrix(alist.getUtil());
3773 RN = JLINE.arraylist_to_matrix(alist.getRespT());
3774 WN = JLINE.arraylist_to_matrix(alist.getResidT());
3775 AN = JLINE.arraylist_to_matrix(alist.getArvR());
3776 TN = JLINE.arraylist_to_matrix(alist.getTput());
3777 end
3778 end
3779
3780 function featSupported = getFeatureSet()
3781 % FEATSUPPORTED = GETFEATURESET()
3782
3783 featSupported = SolverFeatureSet;
3784 featSupported.setTrue({'Sink','Source',...
3785 'ClassSwitch','Delay','DelayStation','Queue',...
3786 'APH','Coxian','Erlang','Exp','HyperExp',...
3787 'StatelessClassSwitcher','InfiniteServer','SharedServer','Buffer','Dispatcher',...
3788 'Server','JobSink','RandomSource','ServiceTunnel',...
3789 'SchedStrategy_INF','SchedStrategy_PS',...
3790 'RoutingStrategy_PROB','RoutingStrategy_RAND',...
3791 'ClosedClass','OpenClass'});
3792 end
3793
3794 function [bool, featSupported] = supports(model)
3795 % [BOOL, FEATSUPPORTED] = SUPPORTS(MODEL)
3796
3797 featUsed = model.getUsedLangFeatures();
3798 featSupported = JLINE.getFeatureSet();
3799 bool = SolverFeatureSet.supports(featSupported, featUsed);
3800 end
3801
3802
3803 function solverOptions = parseSolverOptions(solverOptions, options)
3804 fn = fieldnames(options);
3805 fn2 = fieldnames(solverOptions);
3806 for f = 1:length(fn)
3807 found = 0;
3808 for j = 1:length(fn2)
3809 if strcmp(fn{f}, fn2{j})
3810 found = 1;
3811 switch fn{f}
3812 case 'seed'
3813 solverOptions.seed = options.seed;
3814 case 'samples'
3815 solverOptions.samples = options.samples;
3816 case 'confint'
3817 % Parse confint - can be a level (0.95) or 0 to disable
3818 [confintEnabled, confintLevel] = Solver.parseConfInt(options.confint);
3819 if confintEnabled
3820 solverOptions.confint = confintLevel;
3821 else
3822 solverOptions.confint = 0;
3823 end
3824 case 'method'
3825 solverOptions.method = options.method;
3826 case 'config'
3827 if isfield(options.config,'eventcache')
3828 solverOptions.config.eventcache = options.config.eventcache;
3829 end
3830 if isfield(options.config,'fork_join')
3831 solverOptions.config.fork_join = options.config.fork_join;
3832 end
3833 if isfield(options.config,'highvar')
3834 solverOptions.config.highvar = options.config.highvar;
3835 end
3836 if isfield(options.config,'multiserver')
3837 solverOptions.config.multiserver = options.config.multiserver;
3838 end
3839 if isfield(options.config,'np_priority')
3840 solverOptions.config.np_priority = options.config.np_priority;
3841 end
3842 if isfield(options.config,'warmupfrac') && ~isempty(options.config.warmupfrac) ...
3843 && options.config.warmupfrac > 0
3844 % SSA warmup discard (mean estimates + CI batch means)
3845 solverOptions.config.warmupfrac = java.lang.Double(options.config.warmupfrac);
3846 end
3847 case 'verbose'
3848 switch options.(fn{f})
3849 case {VerboseLevel.SILENT}
3850 solverOptions.verbose = solverOptions.verbose.SILENT;
3851 case {VerboseLevel.STD}
3852 solverOptions.verbose = solverOptions.verbose.STD;
3853 case {VerboseLevel.DEBUG}
3854 solverOptions.verbose = solverOptions.verbose.DEBUG;
3855 end
3856 case 'init_sol'
3857 solverOptions.(fn{f}) = JLINE.from_line_matrix(options.init_sol);
3858 case 'cutoff'
3859 if isscalar(options.cutoff)
3860 solverOptions.(fn{f}) = jline.util.matrix.Matrix.singleton(options.cutoff);
3861 else
3862 solverOptions.(fn{f}) = JLINE.from_line_matrix(options.cutoff);
3863 end
3864 case 'odesolvers'
3865 case 'rewardIterations'
3866 solverOptions.rewardIterations = java.lang.Integer(options.rewardIterations);
3867 otherwise
3868 solverOptions.(fn{f}) = options.(fn{f});
3869 end
3870
3871 break;
3872 end
3873 end
3874 if ~found
3875 line_printf('Could not find option %s in the JLINE options.\n', fn{f});
3876 end
3877 end
3878 end
3879
3880 function [ssa] = SolverSSA(network_object, options)
3881 solverOptions = jline.solvers.SolverOptions(jline.lang.constant.SolverType.SSA);
3882 if nargin>1
3883 solverOptions = JLINE.parseSolverOptions(solverOptions, options);
3884 end
3885 jline.util.Maths.setRandomNumbersMatlab(true);
3886 ssa = jline.solvers.ssa.SolverSSA(network_object, solverOptions);
3887 end
3888
3889 function [qns] = SolverQNS(network_object, options)
3890 solverOptions = jline.solvers.SolverOptions(jline.lang.constant.SolverType.QNS);
3891 if nargin>1
3892 solverOptions = JLINE.parseSolverOptions(solverOptions, options);
3893 end
3894 qns = jline.solvers.wrappers.qns.SolverQNS(network_object, solverOptions);
3895 end
3896
3897 function [mam] = SolverMAM(network_object, options)
3898 solverOptions = jline.solvers.SolverOptions(jline.lang.constant.SolverType.MAM);
3899 if nargin>1
3900 solverOptions = JLINE.parseSolverOptions(solverOptions, options);
3901 end
3902 mam = jline.solvers.mam.SolverMAM(network_object, solverOptions);
3903 end
3904
3905 function [jmt] = SolverJMT(network_object, options)
3906 solverOptions = jline.solvers.SolverOptions(jline.lang.constant.SolverType.JMT);
3907 if nargin>1
3908 solverOptions = JLINE.parseSolverOptions(solverOptions, options);
3909 end
3910 jmt = jline.solvers.wrappers.jmt.SolverJMT(network_object, solverOptions);
3911 end
3912
3913 function [ctmc] = SolverCTMC(network_object, options)
3914 solverOptions = jline.solvers.SolverOptions(jline.lang.constant.SolverType.CTMC);
3915 if nargin>1
3916 solverOptions = JLINE.parseSolverOptions(solverOptions, options);
3917 end
3918 ctmc = jline.solvers.ctmc.SolverCTMC(network_object,solverOptions);
3919 end
3920
3921 function [infGen, eventFilt, syncInfo, stateSpace, nodeStateSpace] = getSymbolicGenerator(ctmc, invertSymbol)
3922 % [INFGEN, EVENTFILT, SYNCINFO, STATESPACE, NODESTATESPACE] = GETSYMBOLICGENERATOR(CTMC, INVERTSYMBOL)
3923 % Symbolic infinitesimal generator of a JLINE SolverCTMC object, with
3924 % each event filtration normalized by its minimum positive rate and
3925 % scaled by a symbolic variable x1..xE, as in the native
3926 % SolverCTMC.getSymbolicGenerator. Coefficient matrices are computed
3927 % by the JAR; symbolic objects are rebuilt with the Symbolic Toolbox.
3928 if nargin<2
3929 invertSymbol = false;
3930 end
3931 if ~exist('sym')
3932 line_error(mfilename,'This method requires MATLAB''s Symbolic Toolbox.');
3933 end
3934 res = ctmc.getSymbolicGenerator(invertSymbol);
3935 stateSpace = JLINE.from_jline_matrix(res.stateSpace);
3936 n = size(stateSpace,1);
3937 nEvents = res.eventFilt.size();
3938 infGen = sym(zeros(n));
3939 eventFilt = cell(1, nEvents);
3940 for e = 1:nEvents
3941 symName = res.symbols.get(e-1);
3942 if ~isempty(symName)
3943 Fe = JLINE.from_jline_matrix(res.eventFilt.get(e-1));
3944 if invertSymbol
3945 eventFilt{e} = Fe / sym(char(symName),'real');
3946 else
3947 eventFilt{e} = Fe * sym(char(symName),'real');
3948 end
3949 infGen = infGen + eventFilt{e};
3950 end
3951 end
3952 infGen = ctmc_makeinfgen(infGen);
3953 syncInfo = res.syncInfo;
3954 nodeStateSpace = cell(1, res.nodeStateSpace.size());
3955 for i = 1:res.nodeStateSpace.size()
3956 nodeStateSpace{i} = JLINE.from_jline_matrix(res.nodeStateSpace.get(i-1));
3957 end
3958 end
3959
3960 function [fluid] = SolverFluid(network_object, options)
3961 solverOptions = jline.solvers.SolverOptions(jline.lang.constant.SolverType.FLUID);
3962 if nargin>1
3963 solverOptions = JLINE.parseSolverOptions(solverOptions, options);
3964 end
3965 fluid = jline.solvers.fluid.SolverFluid(network_object, solverOptions);
3966 end
3967
3968 function [QN, UN, RN, TN, CN, XN, t, QNt, UNt, TNt, xvec] = runFluidAnalyzer(network, options)
3969 % RUNFLUIDANALYZER Run JLINE fluid analyzer and return results
3970 %
3971 % [QN, UN, RN, TN, CN, XN, T, QNT, UNT, TNT, XVEC] = JLINE.runFluidAnalyzer(NETWORK, OPTIONS)
3972 %
3973 % Runs the JLINE fluid solver on the given network and converts
3974 % results back to MATLAB data structures.
3975 %
3976 % Input:
3977 % network - LINE Network model
3978 % options - Solver options structure with fields:
3979 % .method - solver method
3980 % .stiff - use stiff ODE solver
3981 %
3982 % Output:
3983 % QN, UN, RN, TN - Steady-state metrics [M x K]
3984 % CN, XN - System metrics [1 x K]
3985 % t - Time vector [Tmax x 1]
3986 % QNt, UNt, TNt - Transient metrics {M x K} cells
3987 % xvec - State vector structure
3988
3989 jmodel = LINE2JLINE(network);
3990 jsolver = JLINE.SolverFluid(jmodel);
3991 import jline.solvers.fluid.*;
3992
3993 jsolver.options.method = options.method;
3994 jsolver.options.stiff = options.stiff;
3995 result = jsolver.runMethodSpecificAnalyzerViaLINE();
3996
3997 % Convert JLINE result to MATLAB data structures
3998 M = jmodel.getNumberOfStatefulNodes();
3999 K = jmodel.getNumberOfClasses();
4000
4001 QN = NaN * zeros(M, K);
4002 UN = NaN * zeros(M, K);
4003 RN = NaN * zeros(M, K);
4004 TN = NaN * zeros(M, K);
4005 CN = NaN * zeros(1, K);
4006 XN = NaN * zeros(1, K);
4007
4008 QNt = cell(M, K);
4009 UNt = cell(M, K);
4010 TNt = cell(M, K);
4011
4012 Tmax = result.t.length();
4013 t = NaN * zeros(Tmax, 1);
4014
4015 for ist = 1:M
4016 for jst = 1:K
4017 QN(ist, jst) = result.QN.get(ist-1, jst-1);
4018 UN(ist, jst) = result.UN.get(ist-1, jst-1);
4019 RN(ist, jst) = result.RN.get(ist-1, jst-1);
4020 TN(ist, jst) = result.TN.get(ist-1, jst-1);
4021 end
4022 end
4023
4024 for jst = 1:K
4025 CN(1, jst) = result.CN.get(0, jst-1);
4026 XN(1, jst) = result.XN.get(0, jst-1);
4027 end
4028
4029 for ist = 1:M
4030 for jst = 1:K
4031 for p = 1:Tmax
4032 QNt{ist, jst}(p, 1) = result.QNt(ist, jst).get(p-1, 0);
4033 UNt{ist, jst}(p, 1) = result.UNt(ist, jst).get(p-1, 0);
4034 TNt{ist, jst}(p, 1) = result.TNt(ist, jst).get(p-1, 0);
4035 end
4036 end
4037 end
4038
4039 for p = 1:Tmax
4040 t(p, 1) = result.t.get(p-1, 0);
4041 end
4042
4043 % JLINE does not return odeStateVec
4044 xvec.odeStateVec = [];
4045 xvec.sn = network;
4046 end
4047
4048 function [ldes] = SolverLDES(network_object, options)
4049 % Create LDES-specific options object
4050 ldesOptions = jline.solvers.ldes.LDESOptions();
4051 if nargin>1
4052 % Copy standard options
4053 ldesOptions.samples = options.samples;
4054 ldesOptions.seed = options.seed;
4055 % Silence the Java backend at SILENT/STD so its progress,
4056 % transient-filter, CI-method and analysis chatter does not
4057 % duplicate the MATLAB layer's SSA-style summary: MATLAB emits
4058 % the "LDES samples" line (see @SolverLDES/runAnalyzer) and the
4059 % analysis line (@NetworkSolver/setAvgResults, which for LDES
4060 % drops "Iterations" and reports the MATLAB release as env).
4061 % This mirrors the already-clean native-binary path, which
4062 % captures Java stdout instead of echoing it. DEBUG still
4063 % forwards the Java internals for troubleshooting.
4064 if isfield(options, 'verbose')
4065 switch options.verbose
4066 case {VerboseLevel.DEBUG}
4067 ldesOptions.verbose = ldesOptions.verbose.DEBUG;
4068 otherwise
4069 ldesOptions.verbose = ldesOptions.verbose.SILENT;
4070 end
4071 end
4072 % Parse confint
4073 [confintEnabled, confintLevel] = Solver.parseConfInt(options.confint);
4074 if confintEnabled
4075 ldesOptions.confint = confintLevel;
4076 else
4077 ldesOptions.confint = 0;
4078 end
4079 % Pass timespan for transient analysis
4080 if isfield(options, 'timespan') && length(options.timespan) >= 2
4081 ldesOptions.timespan = options.timespan;
4082 end
4083 % Warm-start initial placement (station-major vector, e.g. set
4084 % by @SolverLDES/initFromSolver from an auxiliary solver)
4085 if isfield(options, 'init_sol') && ~isempty(options.init_sol)
4086 ldesOptions.init_sol = JLINE.from_line_matrix(options.init_sol);
4087 end
4088 % Pass LDES-specific options if configured
4089 if isfield(options, 'config')
4090 % Transient detection options
4091 if isfield(options.config, 'tranfilter')
4092 ldesOptions.tranfilter = options.config.tranfilter;
4093 end
4094 if isfield(options.config, 'mserbatch')
4095 ldesOptions.mserbatch = options.config.mserbatch;
4096 end
4097 if isfield(options.config, 'warmupfrac')
4098 ldesOptions.warmupfrac = options.config.warmupfrac;
4099 end
4100 % Confidence interval options
4101 if isfield(options.config, 'cimethod')
4102 ldesOptions.cimethod = options.config.cimethod;
4103 end
4104 if isfield(options.config, 'obmoverlap')
4105 ldesOptions.obmoverlap = options.config.obmoverlap;
4106 end
4107 if isfield(options.config, 'ciminbatch')
4108 ldesOptions.ciminbatch = options.config.ciminbatch;
4109 end
4110 if isfield(options.config, 'ciminobs')
4111 ldesOptions.ciminobs = options.config.ciminobs;
4112 end
4113 if isfield(options.config, 'spectralLowFreqFrac')
4114 ldesOptions.spectralLowFreqFrac = options.config.spectralLowFreqFrac;
4115 end
4116 % Convergence options
4117 if isfield(options.config, 'cnvgon')
4118 ldesOptions.cnvgon = options.config.cnvgon;
4119 end
4120 if isfield(options.config, 'cnvgtol')
4121 ldesOptions.cnvgtol = options.config.cnvgtol;
4122 end
4123 if isfield(options.config, 'cnvgbatch')
4124 ldesOptions.cnvgbatch = options.config.cnvgbatch;
4125 end
4126 if isfield(options.config, 'cnvgchk')
4127 ldesOptions.cnvgchk = options.config.cnvgchk;
4128 end
4129 end
4130 end
4131 ldes = jline.solvers.ldes.SolverLDES(network_object, ldesOptions);
4132 end
4133
4134 function [mva] = SolverMVA(network_object, options)
4135 solverOptions = jline.solvers.SolverOptions(jline.lang.constant.SolverType.MVA);
4136 if nargin>1
4137 solverOptions = JLINE.parseSolverOptions(solverOptions, options);
4138 end
4139 mva = jline.solvers.mva.SolverMVA(network_object, solverOptions);
4140 end
4141
4142 function [nc] = SolverNC(network_object, options)
4143 solverOptions = jline.solvers.SolverOptions(jline.lang.constant.SolverType.NC);
4144 if nargin>1
4145 solverOptions = JLINE.parseSolverOptions(solverOptions, options);
4146 end
4147 nc = jline.solvers.nc.SolverNC(network_object, solverOptions);
4148 end
4149
4150 function [auto] = SolverAuto(network_object, options)
4151 solverOptions = jline.solvers.auto.AUTOptions();
4152 if nargin>1
4153 solverOptions = JLINE.parseSolverOptions(solverOptions, options);
4154 end
4155 auto = jline.solvers.auto.SolverAUTO(network_object, solverOptions);
4156 end
4157
4158 function streamOpts = StreamingOptions(varargin)
4159 % STREAMINGOPTIONS Create Java StreamingOptions for SSA/LDES stream() method
4160 %
4161 % @brief Creates StreamingOptions for streaming simulation metrics
4162 %
4163 % @param varargin Name-value pairs for options:
4164 % 'transport' - 'http' (recommended) or 'grpc' (default: 'http')
4165 % 'endpoint' - Receiver endpoint (default: 'localhost:8080/metrics' for HTTP)
4166 % 'mode' - 'sampled' or 'time_window' (default: 'sampled')
4167 % 'sampleFrequency' - Push every N events in sampled mode (default: 100)
4168 % 'timeWindowSeconds' - Window duration in time_window mode (default: 1.0)
4169 % 'serviceName' - Service identifier (default: 'line-stream')
4170 % 'includeQueueLength' - Include queue length metrics (default: true)
4171 % 'includeUtilization' - Include utilization metrics (default: true)
4172 % 'includeThroughput' - Include throughput metrics (default: true)
4173 % 'includeResponseTime' - Include response time metrics (default: true)
4174 % 'includeArrivalRate' - Include arrival rate metrics (default: true)
4175 %
4176 % @return streamOpts Java StreamingOptions object
4177 %
4178 % Example:
4179 % @code
4180 % streamOpts = JLINE.StreamingOptions('transport', 'http', 'sampleFrequency', 50);
4181 % @endcode
4182
4183 streamOpts = jline.streaming.StreamingOptions();
4184
4185 % Parse optional arguments
4186 p = inputParser;
4187 addParameter(p, 'transport', 'http', @ischar);
4188 addParameter(p, 'endpoint', '', @ischar); % Empty means use default for transport
4189 addParameter(p, 'mode', 'sampled', @ischar);
4190 addParameter(p, 'sampleFrequency', 100, @isnumeric);
4191 addParameter(p, 'timeWindowSeconds', 1.0, @isnumeric);
4192 addParameter(p, 'serviceName', 'line-stream', @ischar);
4193 addParameter(p, 'includeQueueLength', true, @islogical);
4194 addParameter(p, 'includeUtilization', true, @islogical);
4195 addParameter(p, 'includeThroughput', true, @islogical);
4196 addParameter(p, 'includeResponseTime', true, @islogical);
4197 addParameter(p, 'includeArrivalRate', true, @islogical);
4198 parse(p, varargin{:});
4199
4200 % Set transport type
4201 transportTypes = javaMethod('values', 'jline.streaming.StreamingOptions$TransportType');
4202 switch lower(p.Results.transport)
4203 case 'http'
4204 streamOpts.transport = transportTypes(1); % HTTP
4205 case 'grpc'
4206 streamOpts.transport = transportTypes(2); % GRPC
4207 otherwise
4208 streamOpts.transport = transportTypes(1); % Default to HTTP
4209 end
4210
4211 % Set endpoint (use provided or default based on transport)
4212 if ~isempty(p.Results.endpoint)
4213 streamOpts.endpoint = p.Results.endpoint;
4214 end
4215 % If empty, StreamingOptions uses its default for the transport type
4216
4217 % Set mode
4218 streamModes = javaMethod('values', 'jline.streaming.StreamingOptions$StreamMode');
4219 switch lower(p.Results.mode)
4220 case 'sampled'
4221 streamOpts.mode = streamModes(1); % SAMPLED
4222 case 'time_window'
4223 streamOpts.mode = streamModes(2); % TIME_WINDOW
4224 otherwise
4225 streamOpts.mode = streamModes(1); % Default to SAMPLED
4226 end
4227
4228 % Set other options
4229 streamOpts.sampleFrequency = p.Results.sampleFrequency;
4230 streamOpts.timeWindowSeconds = p.Results.timeWindowSeconds;
4231 streamOpts.serviceName = p.Results.serviceName;
4232 streamOpts.includeQueueLength = p.Results.includeQueueLength;
4233 streamOpts.includeUtilization = p.Results.includeUtilization;
4234 streamOpts.includeThroughput = p.Results.includeThroughput;
4235 streamOpts.includeResponseTime = p.Results.includeResponseTime;
4236 streamOpts.includeArrivalRate = p.Results.includeArrivalRate;
4237 end
4238
4239 function result = convertSampleResult(jresult)
4240 % CONVERTSAMPLERESULT Convert Java sample result to MATLAB struct
4241 %
4242 % @brief Converts Java SampleNodeState to MATLAB structure
4243 %
4244 % @param jresult Java SampleNodeState object
4245 % @return result MATLAB struct with fields: t, state, isaggregate
4246
4247 result = struct();
4248
4249 % Convert time matrix
4250 if ~isempty(jresult.t)
4251 result.t = JLINE.from_jline_matrix(jresult.t);
4252 else
4253 result.t = [];
4254 end
4255
4256 % Convert state matrix
4257 if ~isempty(jresult.state) && isa(jresult.state, 'jline.util.matrix.Matrix')
4258 result.state = JLINE.from_jline_matrix(jresult.state);
4259 else
4260 result.state = [];
4261 end
4262
4263 result.isaggregate = jresult.isaggregate;
4264 end
4265
4266 function [ln] = SolverLN(layered_network_object, options)
4267 solverOptions = jline.solvers.SolverOptions(jline.lang.constant.SolverType.LN);
4268 if nargin>1
4269 solverOptions = JLINE.parseSolverOptions(solverOptions, options);
4270 end
4271 ln = jline.solvers.ln.SolverLN(layered_network_object, solverOptions);
4272 end
4273
4274 function jfun = reward_handle_to_tabulatedfun(rewardFn, sn)
4275 % REWARD_HANDLE_TO_TABULATEDFUN Convert a MATLAB reward function
4276 % handle to a Java TabulatedRewardFunction by pre-computing its
4277 % value over an enumerable superset of the aggregated state space.
4278 %
4279 % The domain per class r is: all per-station count vectors with
4280 % sum <= njobs(r) for closed classes, and per-station counts capped
4281 % by classcap(i,r) (or the default CTMC cutoff of 100 when
4282 % infinite) for open classes. The JAR reward analyzer evaluates the
4283 % function only on reachable stateSpaceAggr rows, which are a
4284 % subset of this domain; unseen states raise a descriptive error.
4285 %
4286 % @param rewardFn MATLAB reward handle @(state) or @(state, sn)
4287 % @param sn Network struct
4288 % @return jfun Java jline.lang.reward.TabulatedRewardFunction
4289
4290 M = sn.nstations;
4291 K = sn.nclasses;
4292
4293 % Per-class families of feasible per-station count vectors, with a
4294 % guard against combinatorial explosion (as in the PAS precompute)
4295 classVecs = cell(1, K);
4296 total = 1;
4297 for r = 1:K
4298 if isfinite(sn.njobs(r)) && sn.njobs(r) > 0
4299 classVecs{r} = JLINE.reward_enum_capped(M, sn.njobs(r)*ones(1,M), sn.njobs(r));
4300 else
4301 caps = zeros(1, M);
4302 for i = 1:M
4303 if sn.sched(i) == SchedStrategy.EXT
4304 % Source (arrival) station: it never holds jobs in
4305 % the aggregated CTMC state (its column is 0), so
4306 % enumerating it 0..cutoff both wastes states and
4307 % multiplies the per-class box needlessly. Cap at 0.
4308 caps(i) = 0;
4309 continue;
4310 end
4311 c = sn.classcap(i,r);
4312 if ~isfinite(c)
4313 c = 100; % default CTMC cutoff (see solver_ctmc_reward)
4314 end
4315 caps(i) = c;
4316 end
4317 classVecs{r} = JLINE.reward_enum_capped(M, caps, Inf);
4318 end
4319 total = total * size(classVecs{r},1);
4320 if total > 5e6
4321 line_error(mfilename, 'Reward pre-computation would enumerate more than 5e6 aggregated states; set finite class capacities (or smaller populations) for JLINE conversion.');
4322 end
4323 end
4324
4325 % Index maps for RewardState, as in solver_ctmc_reward
4326 nodeToStationMap = containers.Map('KeyType', 'int32', 'ValueType', 'int32');
4327 classToIndexMap = containers.Map('KeyType', 'int32', 'ValueType', 'int32');
4328 for ind = 1:sn.nnodes
4329 if sn.isstation(ind)
4330 nodeToStationMap(int32(ind)) = sn.nodeToStation(ind);
4331 end
4332 end
4333 for r = 1:K
4334 classToIndexMap(int32(r)) = r;
4335 end
4336
4337 jfun = javaObject('jline.lang.reward.TabulatedRewardFunction');
4338 counts = zeros(1, K);
4339 for r = 1:K
4340 counts(r) = size(classVecs{r}, 1);
4341 end
4342 idx = ones(1, K);
4343 while true
4344 row = zeros(1, M*K);
4345 for r = 1:K
4346 row(((1:M)-1)*K + r) = classVecs{r}(idx(r), :);
4347 end
4348 rewardState = RewardState(row, sn, nodeToStationMap, classToIndexMap);
4349 % Try the new single-argument API first, then the backward
4350 % compatible @(state, sn) signature (as in solver_ctmc_reward)
4351 try
4352 val = rewardFn(rewardState);
4353 catch ME
4354 try
4355 val = rewardFn(row, sn);
4356 catch
4357 rethrow(ME);
4358 end
4359 end
4360 jfun.addValue(JLINE.from_line_matrix(row), double(val));
4361 % Advance the mixed-radix odometer over classes
4362 r = 1;
4363 while r <= K
4364 idx(r) = idx(r) + 1;
4365 if idx(r) <= counts(r)
4366 break;
4367 end
4368 idx(r) = 1;
4369 r = r + 1;
4370 end
4371 if r > K
4372 break;
4373 end
4374 end
4375 end
4376
4377 function V = reward_enum_capped(M, caps, budget)
4378 % REWARD_ENUM_CAPPED All integer row vectors v (1 x M) with
4379 % 0 <= v(i) <= caps(i) and sum(v) <= budget.
4380 if M == 1
4381 hi = min(caps(1), budget);
4382 V = (0:hi)';
4383 return
4384 end
4385 V = zeros(0, M);
4386 hi = min(caps(1), budget);
4387 for n = 0:hi
4388 Vsub = JLINE.reward_enum_capped(M-1, caps(2:end), budget - n);
4389 V = [V; [n*ones(size(Vsub,1),1), Vsub]]; %#ok<AGROW>
4390 end
4391 end
4392
4393 function serfun = pas_handle_to_serializablefun(handle, nclasses, cap)
4394 % PAS_HANDLE_TO_SERIALIZABLEFUN Convert a PAS service rate function
4395 % mu(c) (MATLAB handle of the ordered class list) to a Java
4396 % SerializableFunction by pre-computing mu over every ordered prefix
4397 % up to the queue capacity.
4398 %
4399 % The JAR queries mu(c) with the ordered prefix as a row vector of
4400 % 0-based class indices (jline.lang.state.AfterEventStation), and the
4401 % PrecomputedRateFunction keys on the stringified vector, so values
4402 % are stored under the matching 0-based key.
4403 %
4404 % @param handle MATLAB mu(c) handle taking a 1-based ordered class list
4405 % @param nclasses Number of classes
4406 % @param cap Station capacity (max ordered-list length)
4407 % @return serfun Java PrecomputedCDFunction
4408
4409 % Guard against combinatorial explosion of ordered sequences
4410 nseq = 0; term = 1;
4411 for k = 1:cap
4412 term = term * nclasses;
4413 nseq = nseq + term;
4414 end
4415 if nseq > 5e6
4416 line_error(mfilename, sprintf('PAS service rate pre-computation would enumerate %d ordered states (nclasses=%d, cap=%d); too large for JLINE conversion.', nseq, nclasses, cap));
4417 end
4418
4419 serfun = jline.util.PrecomputedRateFunction(nclasses, 0.0);
4420 JLINE.pas_enumerate_seqs(handle, serfun, nclasses, cap, []);
4421 end
4422
4423 function pas_enumerate_seqs(handle, serfun, nclasses, cap, prefix)
4424 % PAS_ENUMERATE_SEQS Recursively enumerate ordered class sequences
4425 % (1-based, length 1..cap) and store mu(prefix) under the matching
4426 % 0-based key expected by the JAR.
4427 if ~isempty(prefix)
4428 val = handle(prefix); % mu(c), 1-based ordered list
4429 keyMat = JLINE.from_line_matrix(prefix - 1); % 0-based key (1 x p)
4430 serfun.addValue(keyMat, double(val));
4431 end
4432 if length(prefix) >= cap
4433 return;
4434 end
4435 for r = 1:nclasses
4436 JLINE.pas_enumerate_seqs(handle, serfun, nclasses, cap, [prefix, r]);
4437 end
4438 end
4439
4440 function serfun = handle_to_serializablefun(handle, sn)
4441 % HANDLE_TO_SERIALIZABLEFUN Convert MATLAB function handle to Java SerializableFunction
4442 %
4443 % This function pre-computes the function values for all possible state
4444 % combinations and creates a Java PrecomputedCDFunction object.
4445 %
4446 % @param handle MATLAB function handle that takes a vector ni and returns a scalar
4447 % @param sn Network struct containing njobs (population per class)
4448 % @return serfun Java PrecomputedCDFunction object
4449
4450 % Get number of classes and maximum populations
4451 nclasses = sn.nclasses;
4452 njobs = sn.njobs; % Population per class
4453
4454 % For open classes (njobs=0), use a reasonable bound
4455 maxPop = njobs;
4456 for r = 1:nclasses
4457 if maxPop(r) == 0 || isinf(maxPop(r))
4458 % For open classes, use sum of closed class populations or 100 as bound
4459 maxPop(r) = max(100, sum(njobs(isfinite(njobs) & njobs > 0)));
4460 end
4461 end
4462
4463 % Create Java PrecomputedCDFunction object
4464 serfun = jline.util.PrecomputedCDFunction(nclasses);
4465
4466 % Enumerate all possible state combinations and pre-compute function values
4467 % Use recursive enumeration to handle arbitrary number of classes
4468 JLINE.enumerate_states(handle, serfun, maxPop, zeros(1, nclasses), 1);
4469 end
4470
4471 function enumerate_states(handle, serfun, maxPop, currentState, classIdx)
4472 % ENUMERATE_STATES Recursively enumerate all state combinations
4473 %
4474 % @param handle MATLAB function handle
4475 % @param serfun Java PrecomputedCDFunction object to populate
4476 % @param maxPop Maximum population per class
4477 % @param currentState Current state being built
4478 % @param classIdx Current class index being enumerated
4479
4480 nclasses = length(maxPop);
4481
4482 if classIdx > nclasses
4483 % We have a complete state, compute and store the function value.
4484 % Errors are deliberately NOT swallowed here: a state that fails
4485 % to tabulate falls back to the neutral scaling beta=1 on lookup,
4486 % which silently returns the UNSCALED network rather than failing.
4487 value = handle(currentState);
4488 % Convert to Java int array and add to serfun
4489 jstate = jline.util.matrix.Matrix(1, nclasses);
4490 for r = 1:nclasses
4491 jstate.set(0, r-1, currentState(r));
4492 end
4493 if isscalar(value)
4494 % Chain-independent beta_i(n): one scaling shared by every
4495 % class, broadcast on the Java side.
4496 serfun.addValue(jstate, double(value));
4497 else
4498 % Chain-specific beta_{i,r}(n): keep the per-class vector
4499 % whole (the double[] overload). Passing it to the scalar
4500 % addValue would raise, and collapsing it would drop the
4501 % per-class resolution the FES aggregation depends on.
4502 serfun.addValue(jstate, double(value(:)'));
4503 end
4504 return;
4505 end
4506
4507 % Enumerate all populations for current class
4508 for n = 0:maxPop(classIdx)
4509 currentState(classIdx) = n;
4510 JLINE.enumerate_states(handle, serfun, maxPop, currentState, classIdx + 1);
4511 end
4512 end
4513
4514 function result = call_java_cdscaling(jfun, ni)
4515 % CALL_JAVA_CDSCALING Call a Java SerializableFunction for class dependence
4516 %
4517 % This function converts a MATLAB vector to a Java Matrix and calls
4518 % the Java function's apply() method.
4519 %
4520 % @param jfun Java SerializableFunction<Matrix, Double> object
4521 % @param ni MATLAB vector representing the state (jobs per class)
4522 % @return result The scaling factor returned by the Java function
4523
4524 % Convert MATLAB vector to Java Matrix
4525 if isrow(ni)
4526 jmatrix = jline.util.matrix.Matrix(1, length(ni));
4527 for r = 1:length(ni)
4528 jmatrix.set(0, r-1, ni(r));
4529 end
4530 else
4531 jmatrix = jline.util.matrix.Matrix(length(ni), 1);
4532 for r = 1:length(ni)
4533 jmatrix.set(r-1, 0, ni(r));
4534 end
4535 end
4536
4537 % Call the Java function and convert result to MATLAB double
4538 jresult = jfun.apply(jmatrix);
4539 result = double(jresult);
4540 end
4541
4542 function jSched = to_jline_sched_strategy(schedId)
4543 % Convert MATLAB SchedStrategy id to jline SchedStrategy enum
4544 switch schedId
4545 case SchedStrategy.REF
4546 jSched = jline.lang.constant.SchedStrategy.REF;
4547 case SchedStrategy.INF
4548 jSched = jline.lang.constant.SchedStrategy.INF;
4549 case SchedStrategy.FCFS
4550 jSched = jline.lang.constant.SchedStrategy.FCFS;
4551 case SchedStrategy.LCFS
4552 jSched = jline.lang.constant.SchedStrategy.LCFS;
4553 case SchedStrategy.SIRO
4554 jSched = jline.lang.constant.SchedStrategy.SIRO;
4555 case SchedStrategy.SJF
4556 jSched = jline.lang.constant.SchedStrategy.SJF;
4557 case SchedStrategy.LJF
4558 jSched = jline.lang.constant.SchedStrategy.LJF;
4559 case SchedStrategy.PS
4560 jSched = jline.lang.constant.SchedStrategy.PS;
4561 case SchedStrategy.DPS
4562 jSched = jline.lang.constant.SchedStrategy.DPS;
4563 case SchedStrategy.GPS
4564 jSched = jline.lang.constant.SchedStrategy.GPS;
4565 case SchedStrategy.SEPT
4566 jSched = jline.lang.constant.SchedStrategy.SEPT;
4567 case SchedStrategy.LEPT
4568 jSched = jline.lang.constant.SchedStrategy.LEPT;
4569 case SchedStrategy.HOL
4570 % preserve HOL (exact M/G/1 priority in the JAR) rather than
4571 % collapsing to FCFSPRIO (egflin approximation)
4572 jSched = jline.lang.constant.SchedStrategy.HOL;
4573 case SchedStrategy.FCFSPRIO
4574 jSched = jline.lang.constant.SchedStrategy.FCFSPRIO;
4575 case SchedStrategy.FORK
4576 jSched = jline.lang.constant.SchedStrategy.FORK;
4577 case SchedStrategy.EXT
4578 jSched = jline.lang.constant.SchedStrategy.EXT;
4579 case SchedStrategy.LCFSPR
4580 jSched = jline.lang.constant.SchedStrategy.LCFSPR;
4581 case SchedStrategy.PSPRIO
4582 jSched = jline.lang.constant.SchedStrategy.PSPRIO;
4583 case SchedStrategy.DPSPRIO
4584 jSched = jline.lang.constant.SchedStrategy.DPSPRIO;
4585 case SchedStrategy.GPSPRIO
4586 jSched = jline.lang.constant.SchedStrategy.GPSPRIO;
4587 otherwise
4588 jSched = jline.lang.constant.SchedStrategy.FCFS;
4589 end
4590 end
4591
4592 function tf = is_custom_handle(funCell, e, h, defaultStr)
4593 % IS_CUSTOM_HANDLE True if funCell{e,h} is a function handle that
4594 % differs from the given identity default (whitespace-insensitive
4595 % func2str comparison).
4596 tf = false;
4597 if isempty(funCell) || size(funCell,1) < e || size(funCell,2) < h
4598 return;
4599 end
4600 fh = funCell{e,h};
4601 if isempty(fh) || ~isa(fh, 'function_handle')
4602 return;
4603 end
4604 tf = ~strcmp(regexprep(func2str(fh), '\s+', ''), defaultStr);
4605 end
4606
4607 function jwf = from_line_workflow(line_wf)
4608 % Convert a MATLAB Workflow to a JAR Workflow.
4609 jwf = javaObject('jline.lang.workflow.Workflow', java.lang.String(line_wf.getName()));
4610 acts = line_wf.activities;
4611 for a = 1:length(acts)
4612 act = acts{a};
4613 actName = java.lang.String(act.name);
4614 if ~isempty(act.hostDemand) && isa(act.hostDemand, 'Distribution')
4615 jdist = JLINE.from_line_distribution(act.hostDemand);
4616 jwf.addActivity(actName, jdist);
4617 else
4618 jwf.addActivity(actName, 1.0);
4619 end
4620 end
4621 precs = line_wf.precedences;
4622 for p = 1:length(precs)
4623 prec = precs(p);
4624 preActs = java.util.ArrayList();
4625 for k = 1:length(prec.preActs)
4626 preActs.add(java.lang.String(prec.preActs{k}));
4627 end
4628 postActs = java.util.ArrayList();
4629 for k = 1:length(prec.postActs)
4630 postActs.add(java.lang.String(prec.postActs{k}));
4631 end
4632 preTypeStr = java.lang.String(ActivityPrecedenceType.toText(prec.preType));
4633 postTypeStr = java.lang.String(ActivityPrecedenceType.toText(prec.postType));
4634 if ~isempty(prec.preParams)
4635 preParamsMat = JLINE.from_line_matrix(prec.preParams(:)');
4636 else
4637 preParamsMat = javaObject('jline.util.matrix.Matrix', 0, 0);
4638 end
4639 if ~isempty(prec.postParams)
4640 postParamsMat = JLINE.from_line_matrix(prec.postParams(:)');
4641 else
4642 postParamsMat = javaObject('jline.util.matrix.Matrix', 0, 0);
4643 end
4644 jprec = javaObject('jline.lang.layered.ActivityPrecedence', ...
4645 preActs, postActs, preTypeStr, postTypeStr, preParamsMat, postParamsMat);
4646 jwf.addPrecedence(jprec);
4647 end
4648 end
4649
4650 function jenv = from_line_environment(line_env)
4651 % Convert a MATLAB Environment to a JAR Environment.
4652 E = height(line_env.envGraph.Nodes);
4653 jenv = javaObject('jline.lang.Environment', java.lang.String(line_env.getName()), int32(E));
4654 for e = 1:E
4655 stageName = char(line_env.envGraph.Nodes.Name{e});
4656 stageType = char(line_env.envGraph.Nodes.Type{e});
4657 stageModel = line_env.ensemble{e};
4658 jmodel = JLINE.from_line_network(stageModel);
4659 jenv.addStage(int32(e-1), java.lang.String(stageName), java.lang.String(stageType), jmodel);
4660 end
4661 if ~isempty(line_env.env)
4662 [Erows, Ecols] = size(line_env.env);
4663 for e = 1:Erows
4664 for h = 1:Ecols
4665 d = line_env.env{e,h};
4666 if isempty(d) || isa(d, 'Disabled')
4667 continue;
4668 end
4669 % Custom reset functions are MATLAB handles over a
4670 % continuous domain and cannot be marshalled to the JAR
4671 % functional interfaces; error out rather than silently
4672 % dropping a non-identity reset policy.
4673 if JLINE.is_custom_handle(line_env.resetFun, e, h, '@(q)q') ...
4674 || JLINE.is_custom_handle(line_env.resetEnvRatesFun, e, h, '@(originalDist,QExit,UExit,TExit)originalDist') ...
4675 || JLINE.is_custom_handle(line_env.resetStateFun, e, h, '@(pi)pi')
4676 line_error(mfilename, sprintf('JLINE conversion cannot marshal the custom reset function on Environment transition %d->%d (resetFun/resetEnvRatesFun/resetStateFun); use the MATLAB-native SolverENV for this model.', e, h));
4677 end
4678 jdist = JLINE.from_line_distribution(d);
4679 jenv.addTransition(int32(e-1), int32(h-1), jdist);
4680 end
4681 end
4682 end
4683 jenv.init();
4684 end
4685
4686 end
4687end
Definition Station.m:287
Definition fjtag.m:157
Definition Station.m:245