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