LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
LQN2JAVA.m
1function LQN2JAVA(lqnmodel, modelName, fid)
2% LQN2JAVA(MODEL, MODELNAME, FID)
3
4% Copyright (c) 2012-2026, Imperial College London
5% All rights reserved.
6
7if nargin<2%~exist('modelName','var')
8 modelName='myLayeredModel';
9end
10if nargin<3%~exist('fid','var')
11 fid=1;
12end
13if ischar(fid)
14 fid = fopen(fid,'w');
15end
16sn = lqnmodel.getStruct;
17
18%% initialization
19fprintf(fid,'package jline.examples;\n\n');
20fprintf(fid,'import jline.lang.*;\n');
21fprintf(fid,'import jline.lang.constant.*;\n');
22fprintf(fid,'import jline.lang.processes.*;\n');
23fprintf(fid,'import jline.solvers.ln.SolverLN;\n\n');
24fprintf(fid,'public class TestSolver%s {\n\n',upper(strrep(modelName,' ','')));
25fprintf(fid,'\tpublic static void main(String[] args) throws Exception{\n\n');
26fprintf(fid,'\tLayeredNetwork model = new LayeredNetwork("%s");\n',modelName);
27fprintf(fid,'\n');
28%% host processors
29for h=1:sn.nhosts
30 if isinf(sn.mult(h))
31 fprintf(fid, '\tProcessor P%d = new Processor(model, "%s", Integer.MAX_VALUE, %s);\n', h, sn.names{h}, strrep(SchedStrategy.toFeature(sn.sched(h)),'_','.'));
32 else
33 fprintf(fid, '\tProcessor P%d = new Processor(model, "%s", %d, %s);\n', h, sn.names{h}, sn.mult(h), strrep(SchedStrategy.toFeature(sn.sched(h)),'_','.'));
34 end
35 if sn.repl(h)~=1
36 fprintf(fid, 'P%d.setReplication(%d);\n', h, sn.repl(h));
37 end
38end
39fprintf(fid,'\n');
40%% tasks
41for t=1:sn.ntasks
42 tidx = sn.tshift+t;
43 if isinf(sn.mult(tidx))
44 fprintf(fid, '\tTask T%d = new Task(model, "%s", Integer.MAX_VALUE, %s).on(P%d);\n', t, sn.names{tidx}, strrep(SchedStrategy.toFeature(sn.sched(tidx)),'_','.'), sn.parent(tidx));
45 else
46 fprintf(fid, '\tTask T%d = new Task(model, "%s", %d, %s).on(P%d);\n', t, sn.names{tidx}, sn.mult(tidx), strrep(SchedStrategy.toFeature(sn.sched(tidx)),'_','.'), sn.parent(tidx));
47 end
48 if sn.repl(tidx)~=1
49 fprintf(fid, '\tT%d.setReplication(%d);\n', t, sn.repl(tidx));
50 end
51 if ~isempty(sn.think{tidx}) && sn.think_type(tidx) ~= ProcessType.DISABLED
52 switch sn.think_type(tidx)
53 case ProcessType.IMMEDIATE
54 fprintf(fid, '\tT%d.setThinkTime(new Immediate());\n',t);
55 case ProcessType.EXP
56 fprintf(fid, '\tT%d.setThinkTime(new Exp(%g));\n',t, 1/sn.think_mean(tidx));
57 case {ProcessType.ERLANG, ProcessType.HYPEREXP, ProcessType.COXIAN, ProcessType.APH}
58 fprintf(fid, '\tT%d.setThinkTime(%s.fitMeanAndSCV(%g,%g));\n', t, char(sn.think_type(tidx)), sn.think_mean(tidx), sn.think_scv(tidx));
59 otherwise
60 line_error(mfilename,sprintf('LQN2JAVA does not support the %s distribution yet.',char(sn.think_type(tidx))));
61 end
62 end
63end
64fprintf(fid,'\n');
65%% entries
66for e=1:sn.nentries
67 eidx = sn.eshift+e;
68 fprintf(fid, '\tEntry E%d = new Entry(model, "%s").on(T%d);\n', e, sn.names{eidx},sn.parent(eidx)-sn.tshift);
69end
70fprintf(fid,'\n');
71%% activities
72for a=1:sn.nacts
73 aidx = sn.ashift+a;
74 tidx = sn.parent(aidx);
75 onTask = tidx-sn.tshift;
76 boundTo = find(sn.graph((sn.eshift+1):(sn.eshift+sn.nentries),aidx));
77 boundToStr = '';
78 if ~isempty(boundTo)
79 boundToStr = sprintf('.boundTo(E%d)',boundTo);
80 end
81 repliesToStr = '';
82 if sn.sched(tidx) ~= SchedStrategy.REF % ref tasks don't reply
83 repliesTo = find(sn.replygraph(a,:)); % index of entry
84 if ~isempty(repliesTo)
85 if ~sn.isref(sn.parent(sn.eshift+repliesTo))
86 repliesToStr = sprintf('.repliesTo(E%d)',repliesTo);
87 end
88 end
89 end
90 callStr = '';
91 if ~isempty(sn.callpair)
92 cidxs = find(sn.callpair(:,1)==aidx);
93 calls = sn.callpair(:,2);
94 for c=cidxs(:)'
95 switch sn.calltype(c)
96 case CallType.SYNC
97 callStr = sprintf('%s.synchCall(E%d,%g)',callStr,calls(c)-sn.eshift,sn.callproc_mean(c));
98 case CallType.ASYNC
99 callStr = sprintf('%s.asynchCall(E%d,%g)',callStr,calls(c)-sn.eshift,sn.callproc_mean(c));
100 end
101 end
102 end
103 switch sn.hostdem_type(aidx)
104 case ProcessType.IMMEDIATE
105 fprintf(fid, '\tActivity A%d = new Activity(model, "%s", new Immediate()).on(T%d);', a, sn.names{aidx}, onTask);
106 case ProcessType.EXP
107 fprintf(fid, '\tActivity A%d = new Activity(model, "%s", new Exp(%g)).on(T%d);', a, sn.names{aidx},1/sn.hostdem_mean(aidx), onTask);
108 case {ProcessType.ERLANG, ProcessType.HYPEREXP, ProcessType.COXIAN, ProcessType.APH}
109 fprintf(fid, '\tActivity A%d = new Activity(model, "%s", %s.fitMeanAndSCV(%g,%g)).on(T%d);', a, sn.names{aidx},char(sn.hostdem_type(aidx)),sn.hostdem_mean(aidx),sn.hostdem_scv(aidx), onTask);
110 otherwise
111 line_error(mfilename,sprintf('LQN2JAVA does not support the %s distribution yet.',char(sn.hostdem_type(aidx))));
112 end
113 if ~isempty(boundToStr)
114 fprintf(fid, ' A%d%s;', a, boundToStr);
115 end
116 if ~isempty(callStr)
117 fprintf(fid, ' A%d%s;', a, callStr);
118 end
119 if ~isempty(repliesToStr)
120 fprintf(fid, ' A%d%s;', a, repliesToStr);
121 end
122 fprintf(fid,'\n');
123end
124fprintf(fid,'\n');
125%% think times
126for h=1:sn.nhosts
127 if ~isempty(sn.think{h}) && sn.think_type(h) ~= ProcessType.DISABLED
128 switch sn.think_type(h)
129 case ProcessType.IMMEDIATE
130 fprintf(fid, '\tP%d.setThinkTime(Immediate());\n', h);
131 case ProcessType.EXP
132 fprintf(fid, '\tP%d.setThinkTime(Exp(%g));\n', h, sn.think_mean(h));
133 case {ProcessType.ERLANG, ProcessType.HYPEREXP, ProcessType.COXIAN, ProcessType.APH}
134 fprintf(fid, '\tP%d.setThinkTime(%s.fitMeanAndSCV(%g,%g));\n', h, char(sn.think_type(h)), sn.think_mean(h), sn.think_scv(h));
135 otherwise
136 line_error(mfilename,sprintf('LQN2JAVA does not support the %s distribution yet.',char(sn.think_type(h))));
137 end
138 end
139end
140
141%% Sequential precedences
142for ai = 1:sn.nacts
143 aidx = sn.ashift + ai;
144 tidx = sn.parent(aidx);
145 % for all successors
146 for bidx=find(sn.graph(aidx,:))
147 if bidx > sn.ashift % ignore precedence between entries and activities
148 % Serial pattern (SEQ)
149 if full(sn.actpretype(aidx)) == ActivityPrecedenceType.PRE_SEQ && full(sn.actposttype(bidx)) == ActivityPrecedenceType.POST_SEQ
150 fprintf(fid, '\tT%d.addPrecedence(ActivityPrecedence.Serial("%s", "%s"));\n', tidx-sn.tshift, sn.names{aidx}, sn.names{bidx});
151 end
152 end
153 end
154end
155
156%% Loop precedences (POST_LOOP)
157% see _kb/12-interfaces-and-docs.md (LQN2JAVA.m loop-precedence encoding)
158hasPreActs = false;
159hasPostActs = false;
160processedLoops = false(1, sn.nacts);
161for ai = 1:sn.nacts
162 aidx = sn.ashift + ai;
163 tidx = sn.parent(aidx);
164 % Check if this activity starts a loop (has a successor with POST_LOOP type)
165 % and hasn't been processed as part of another loop
166 if processedLoops(ai)
167 continue;
168 end
169
170 successors = find(sn.graph(aidx,:));
171 for bidx = successors
172 if bidx > sn.ashift && full(sn.actposttype(bidx)) == ActivityPrecedenceType.POST_LOOP
173 % Skip if this loop body activity was already processed
174 if processedLoops(bidx - sn.ashift)
175 continue;
176 end
177 % Found start of a loop: aidx is the entry, bidx is first loop body activity
178 loopStart = bidx;
179 precMarker = ai;
180 loopActNames = {};
181
182 % Follow the chain of POST_LOOP activities
183 curIdx = loopStart;
184 while true
185 loopActNames{end+1} = sn.names{curIdx}; %#ok<AGROW>
186 processedLoops(curIdx - sn.ashift) = true;
187
188 % Find successors of current activity
189 curSuccessors = find(sn.graph(curIdx,:));
190 curSuccessors = curSuccessors(curSuccessors > sn.ashift);
191
192 % Check for loop termination: find the end activity
193 % End activity has weight = 1/counts (not the back-edge weight)
194 endIdx = 0;
195 nextIdx = 0;
196 for succIdx = curSuccessors
197 if full(sn.actposttype(succIdx)) == ActivityPrecedenceType.POST_LOOP
198 if succIdx == loopStart
199 % This is the back-edge, skip it
200 continue;
201 end
202 weight = full(sn.graph(curIdx, succIdx));
203 if weight > 0 && weight < 1
204 % This is the end activity (weight = 1/counts)
205 endIdx = succIdx;
206 else
207 % This is the next activity in the loop body (weight = 1.0)
208 nextIdx = succIdx;
209 end
210 end
211 end
212
213 if endIdx > 0
214 % Found end activity - calculate counts and output
215 weight = full(sn.graph(curIdx, endIdx));
216 if weight > 0
217 counts = 1/weight;
218 else
219 counts = 1; % Fallback to prevent division by zero
220 end
221 loopActNames{end+1} = sn.names{endIdx}; %#ok<AGROW>
222 processedLoops(endIdx - sn.ashift) = true;
223
224 % Build the precActs string
225 fprintf(fid, '\n\t// Loop Activity Precedence \n');
226 if ~hasPreActs
227 fprintf(fid, '\tArrayList<String> precActs = new ArrayList<String>();\n');
228 hasPreActs = true;
229 else
230 fprintf(fid, '\tprecActs = new ArrayList<String>();\n');
231 end
232 for k = 1:length(loopActNames)
233 fprintf(fid, '\tprecActs.add("%s");\n', loopActNames{k});
234 end
235 fprintf(fid, '\tT%d.addPrecedence(ActivityPrecedence.Loop("%s", precActs, Matrix.singleton(%g)));\n', tidx-sn.tshift, sn.names{aidx}, counts);
236 break;
237 elseif nextIdx > 0
238 % Continue to next activity in loop body
239 curIdx = nextIdx;
240 else
241 % No more successors - shouldn't happen in valid loop
242 break;
243 end
244 end
245 break; % Only process one loop starting from this activity
246 end
247 end
248end
249
250%% OrFork precedences (POST_OR)
251hasProbs = false;
252precMarker = 0;
253precActs = '';
254for ai = 1:sn.nacts
255 aidx = sn.ashift + ai;
256 tidx = sn.parent(aidx);
257 prob_ctr = 0;
258 % for all successors
259 for bidx=find(sn.graph(aidx,:))
260 if bidx > sn.ashift % ignore precedence between entries and activities
261 % Or pattern (POST_OR)
262 if full(sn.actposttype(bidx)) == ActivityPrecedenceType.POST_OR
263 if precMarker == 0 % start a new orjoin
264 precActs = '';
265 precMarker = aidx-sn.ashift;
266 prob_ctr = prob_ctr + 1;
267 precActs = sprintf('\tprecActs.add("%s");\n', sn.names{bidx});
268 probString = sprintf('\tprobs.set(0,%d,%g);\n', prob_ctr-1, full(sn.graph(aidx,bidx)));
269 else
270 prob_ctr = prob_ctr + 1;
271 precActs = sprintf('%s\tprecActs.add("%s");\n', precActs, sn.names{bidx});
272 probString = sprintf('%s\tprobs.set(0,%d,%g);\n', probString, prob_ctr-1, full(sn.graph(aidx,bidx)));
273 end
274 end
275 end
276 end
277
278
279 if precMarker > 0
280 fprintf(fid, '\n\t// OrFork Activity Precedence \n');
281 if ~hasPreActs
282 fprintf(fid, '\tArrayList<String> precActs = new ArrayList<String>();\n');
283 hasPreActs = true;
284 else
285 fprintf(fid, '\tprecActs = new ArrayList<String>();\n');
286 end
287 if ~hasProbs
288 fprintf(fid, '\tMatrix probs = new Matrix(1,%d);\n',prob_ctr);
289 hasProbs = true;
290 else
291 fprintf(fid, '\tprobs = new Matrix(1,%d);\n',prob_ctr);
292 end
293 fprintf(fid, precActs);
294 fprintf(fid, probString);
295 fprintf(fid, '\tT%d.addPrecedence(ActivityPrecedence.OrFork("%s", precActs, probs));\n', tidx-sn.tshift, sn.names{precMarker+sn.ashift});
296 precMarker = 0;
297 end
298end
299
300%% AndFork precedences (POST_AND)
301precMarker = 0;
302postActs = '';
303for ai = 1:sn.nacts
304 aidx = sn.ashift + ai;
305 tidx = sn.parent(aidx);
306 % for all successors
307 for bidx=find(sn.graph(aidx,:))
308 if bidx > sn.ashift % ignore precedence between entries and activities
309 % Or pattern (POST_AND)
310 if full(sn.actposttype(bidx)) == ActivityPrecedenceType.POST_AND
311 if isempty(postActs)
312 postActs = sprintf('\tpostActs.add("%s");\n', sn.names{bidx});
313 else
314 postActs = sprintf('%s\tpostActs.add("%s");\n', postActs, sn.names{bidx});
315 end
316
317 if precMarker == 0 % start a new orjoin
318 precMarker = aidx-sn.ashift;
319 end
320 end
321 end
322 end
323 if precMarker > 0
324 fprintf(fid, '\n\t// AndFork Activity Precedence \n');
325 if ~hasPostActs
326 fprintf(fid, '\tArrayList<String> postActs = new ArrayList<String>();\n');
327 hasPostActs = true;
328 else
329 fprintf(fid, '\t postActs = new ArrayList<String>();\n');
330 end
331
332 fprintf(fid, postActs);
333 fprintf(fid, '\tT%d.addPrecedence(ActivityPrecedence.AndFork("%s", postActs));\n', tidx-sn.tshift, sn.names{precMarker+sn.ashift});
334 precMarker = 0;
335 end
336end
337
338
339%% OrJoin precedences (PRE_OR)
340precMarker = 0;
341for bi = sn.nacts:-1:1
342 bidx = sn.ashift + bi;
343 tidx = sn.parent(bidx);
344 % for all predecessors
345 for aidx=find(sn.graph(:,bidx))'
346 if aidx > sn.ashift % ignore precedence between entries and activities
347 % OrJoin pattern (PRE_OR)
348 if full(sn.actpretype(aidx)) == ActivityPrecedenceType.PRE_OR
349 if precMarker == 0 % start a new orjoin
350 precMarker = bidx-sn.ashift;
351 precActs = sprintf('\tprecActs.add("%s");\n', sn.names{aidx});
352 else
353 precActs = sprintf('%s\tprecActs.add("%s");\n', precActs, sn.names{aidx});
354 end
355 end
356 end
357 end
358 if precMarker > 0
359 fprintf(fid, '\n\t// OrJoin Activity Precedence \n');
360 if ~hasPreActs
361 fprintf(fid, '\tArrayList<String> precActs = new ArrayList<String>();\n');
362 hasPreActs = true;
363 else
364 fprintf(fid, '\tprecActs = new ArrayList<String>();\n');
365 end
366
367 fprintf(fid, precActs);
368 fprintf(fid, '\tT%d.addPrecedence(ActivityPrecedence.OrJoin(precActs, "%s"));\n', tidx-sn.tshift, sn.names{precMarker+sn.ashift});
369 precMarker = 0;
370 end
371end
372
373%% AndJoin precedences (PRE_AND)
374precMarker = 0;
375for bi = sn.nacts:-1:1
376 bidx = sn.ashift + bi;
377 tidx = sn.parent(bidx);
378 % for all predecessors
379 for aidx=find(sn.graph(:,bidx))'
380 if aidx > sn.ashift % ignore precedence between entries and activities
381 % OrJoin pattern (PRE_AND)
382 if full(sn.actpretype(aidx)) == ActivityPrecedenceType.PRE_AND
383 if precMarker == 0 % start a new orjoin
384 precMarker = bidx-sn.ashift;
385 precActs = sprintf('\tprecActs.add("%s");\n', sn.names{aidx});
386 else
387 precActs = sprintf('%s\tprecActs.add("%s");\n', precActs, sn.names{aidx});
388 end
389 end
390 end
391 end
392 if precMarker > 0
393 fprintf(fid, '\n\t// AndJoin Activity Precedence \n');
394 if ~hasPreActs
395 fprintf(fid, '\tArrayList<String> precActs = new ArrayList<String>();\n');
396 hasPreActs = true;
397 else
398 fprintf(fid, '\tprecActs = new ArrayList<String>();\n');
399 end
400 fprintf(fid, precActs);
401
402 % Find quorum parameter from original precedence structure
403 postActName = sn.names{precMarker+sn.ashift};
404 localTaskIdx = tidx - sn.tshift;
405 quorum = [];
406 for ap = 1:length(lqnmodel.tasks{localTaskIdx}.precedences)
407 precedence = lqnmodel.tasks{localTaskIdx}.precedences(ap);
408 if precedence.preType == ActivityPrecedenceType.PRE_AND
409 % Check if this precedence contains our post activity
410 if any(strcmp(precedence.postActs, postActName))
411 quorum = precedence.preParams;
412 break;
413 end
414 end
415 end
416
417 if isempty(quorum)
418 fprintf(fid, '\tT%d.addPrecedence(ActivityPrecedence.AndJoin(precActs, "%s"));\n', localTaskIdx, postActName);
419 else
420 fprintf(fid, '\tT%d.addPrecedence(ActivityPrecedence.AndJoin(precActs, "%s", %g));\n', localTaskIdx, postActName, quorum);
421 end
422 precMarker = 0;
423 end
424end
425
426fprintf(fid, '\n\t// Model solution \n');
427fprintf(fid,'\tSolverLN solver = new SolverLN(model);\n');
428fprintf(fid,'\tsolver.getEnsembleAvg();\n');
429fprintf(fid,'\t}\n}\n');
430
431if ischar(fid)
432 fclose(fid);
433end
434end