LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_qns.m
1function [QN,UN,RN,TN,CN,XN,actualMethod] = solver_qns(sn, options)
2% [Q,U,R,T,C,X] = SOLVER_QNS(QN, OPTIONS)
3
4% Copyright (c) 2012-2026, Imperial College London
5% All rights reserved.
6
7M = sn.nstations; % number of stations
8K = sn.nclasses; % number of classes
9
10QN = zeros(M,K);
11UN = zeros(M,K);
12RN = zeros(M,K);
13TN = zeros(M,K);
14CN = zeros(1,K);
15XN = zeros(1,K);
16
17filePath = lineTempName('qns');
18fileName = 'model';
19fname = [fileName,'.jmva'];
20logFileName = 'console';
21logfname = [logFileName,'.out'];
22outputFileName = [filePath,filesep,fname];
23outputFileName = SolverJMT.writeJMVA(sn, outputFileName, options);
24fileName = 'result';
25ofname = [fileName,'.jmva'];
26resultFileName = [filePath,filesep,ofname];
27%stationames={sn.nodenames{sn.isstation}};
28
29% Track the actual method that will be used
30actualMethod = options.method;
31
32line_debug('QNS solver starting: nstations=%d, nclasses=%d, method=%s', M, K, options.method);
33
34switch options.method
35 case 'conway'
36 options.config.multiserver='conway';
37 case 'reiser'
38 options.config.multiserver='reiser';
39 case 'rolia'
40 options.config.multiserver='rolia';
41 case 'zhou'
42 options.config.multiserver='zhou';
43end
44if any(sn.nservers>1 & sn.nservers<Inf)
45 line_debug('Multi-server queues detected, selecting multiserver method');
46 switch options.config.multiserver
47 case {'default','conway'}
48 if strcmpi(options.config.multiserver, 'default')
49 line_debug('Default method: using Conway multiserver approximation\n');
50 end
51 line_debug('Using Conway multiserver approximation');
52 if ispc
53 cmd=['qnsolver -l ',outputFileName,' -mconway -o ',resultFileName,' > ',logfname];
54 else
55 cmd=['qnsolver -l ',outputFileName,' -mconway -o ',resultFileName,' > ',logfname,' 2>&1'];
56 end
57 actualMethod = 'conway';
58 case 'reiser'
59 line_debug('Using Reiser multiserver approximation');
60 if ispc
61 cmd=['qnsolver -l ',outputFileName,' -mreiser -o ',resultFileName,' > ',logfname];
62 else
63 cmd=['qnsolver -l ',outputFileName,' -mreiser -o ',resultFileName,' > ',logfname,' 2>&1'];
64 end
65 actualMethod = 'reiser';
66 case 'rolia'
67 line_debug('Using Rolia multiserver approximation');
68 if ispc
69 cmd=['qnsolver -l ',outputFileName,' -mrolia -o ',resultFileName,' > ',logfname];
70 else
71 cmd=['qnsolver -l ',outputFileName,' -mrolia -o ',resultFileName,' > ',logfname,' 2>&1'];
72 end
73 actualMethod = 'rolia';
74 case 'zhou'
75 line_debug('Using Zhou multiserver approximation');
76 if ispc
77 cmd=['qnsolver -l ',outputFileName,' -mzhou -o ',resultFileName,' > ',logfname];
78 else
79 cmd=['qnsolver -l ',outputFileName,' -mzhou -o ',resultFileName,' > ',logfname,' 2>&1'];
80 end
81 actualMethod = 'zhou';
82 end
83else
84 if ispc
85 cmd=['qnsolver -l ',outputFileName,' -o ',resultFileName,' > ',logfname];
86 else
87 cmd=['qnsolver -l ',outputFileName,' -o ',resultFileName,' > ',logfname,' 2>&1'];
88 end
89end
90
91% MATLAB prepends its bundled runtime libraries to LD_LIBRARY_PATH before
92% spawning child processes. Its libstdc++ (e.g. GLIBCXX_3.4.28 in R2024a) is
93% older than the GLIBCXX_3.4.29+ required by qnsolver's liblqio/liblqx
94% dependencies, so qnsolver aborts before writing its result file and the
95% parse below would then silently yield all-zero metrics. Strip
96% LD_LIBRARY_PATH for the child so it resolves the system libstdc++ (the same
97% fix applied to lqns/lqsim in SolverLQNS/runAnalyzer.m).
98if isunix
99 cmd = ['env -u LD_LIBRARY_PATH ', cmd];
100end
101if GlobalConstants.Verbose == VerboseLevel.DEBUG
102 line_printf('SolverQNS command:\n');
103 disp(cmd)
104end
105system(cmd);
106name = cell(sn.nstations*(sn.nchains+1),0);
107Uchain = zeros(0,sn.nchains);
108Qchain = zeros(0,sn.nchains);
109Wchain = zeros(0,sn.nchains);
110Tchain = zeros(0,sn.nchains);
111[Lchain,STchain,Vchain,alpha,~,~,~] = sn_get_demands_chain(sn);
112%Lchain = zeros(sn.nstations,sn.nchains); % uncomment for starred output
113i = 1;
114statName = {};
115try
116 fid=fopen(resultFileName,'r');
117 strline = 1;
118 while strline>0
119 strline = fgetl(fid);
120 if sn.nclasses==1
121 [Uchain, Qchain, Wchain, Tchain, statlabel] = parse_dollar_output_singleclass(strline, Uchain, Qchain, Wchain, Tchain);
122 else
123 [Uchain, Qchain, Wchain, Tchain, statlabel] = parse_dollar_output(strline, Uchain, Qchain, Wchain, Tchain);
124 end
125 if ~isempty(statlabel)
126 statName{end+1} = statlabel;
127 end
128 %[Lchain, Uchain, Qchain, Wchain, Tchain] = parse_starred_output(strline, Lchain, Uchain, Qchain, Wchain, Tchain);
129 end
130 fclose(fid);
131catch
132 line_warning(mfilename,'Failed execution: cannot open the qnsolver output file at: ');
133 line_warning(mfilename,resultFileName)
134 QN = nan(M,K);
135 UN = nan(M,K);
136 RN = nan(M,K);
137 TN = nan(M,K);
138 CN = nan(1,K);
139 XN = nan(1,K);
140 return
141end
142
143% Build reorder mapping: maps model station index to qnsolver output row
144% Initialize with zeros (will remain 0 for stations not found in qnsolver output)
145stationToOutputRow = zeros(1, sn.nnodes);
146for i=1:length(statName)
147 idx = find(cellfun(@(x)strcmp(x,statName{i}),sn.nodenames));
148 if ~isempty(idx)
149 stationToOutputRow(idx) = i;
150 end
151end
152
153% Create reordered arrays for stations only
154% Stations are the first sn.nstations nodes
155UchainReordered = zeros(sn.nstations, sn.nchains);
156QchainReordered = zeros(sn.nstations, sn.nchains);
157WchainReordered = zeros(sn.nstations, sn.nchains);
158TchainReordered = zeros(sn.nstations, sn.nchains);
159
160for i=1:sn.nstations
161 outputRow = stationToOutputRow(i);
162 if outputRow > 0
163 UchainReordered(i,:) = Uchain(outputRow,:);
164 QchainReordered(i,:) = Qchain(outputRow,:);
165 WchainReordered(i,:) = Wchain(outputRow,:);
166 TchainReordered(i,:) = Tchain(outputRow,:);
167 end
168end
169
170Uchain = UchainReordered;
171Qchain = QchainReordered;
172Wchain = WchainReordered;
173Tchain = TchainReordered;
174
175ref= zeros(sn.nchains,1);
176for c=1:sn.nchains
177 chain = find(sn.chains(c,:));
178 Xchain(c)=Tchain(sn.refstat(c),c);
179 if Xchain(c) == 0
180 % For open chains where refstat is Source (not in qnsolver output),
181 % recover Xchain from any station with valid throughput:
182 % Xchain(c) = Tchain(i,c) / Vchain(i,c)
183 for i=1:sn.nstations
184 if Vchain(i,c) > 0 && Tchain(i,c) > 0
185 Xchain(c) = Tchain(i,c) / Vchain(i,c);
186 break;
187 end
188 end
189 end
190end
191%Rchain=Wchain./Vchain; % needs to be reinstated for starred output
192Rchain=Wchain;
193Rchain(isnan(Rchain))=0;
194for i=1:sn.nstations
195 if ~isinf(sn.nservers(i))
196 Uchain(i,:) = Uchain(i,:) / sn.nservers(i);
197 end
198end
199
200[QN,UN,RN,TN,CN,XN] = sn_deaggregate_chain_results(sn, Lchain, [], STchain, Vchain, alpha, Qchain, Uchain, Rchain, Tchain, [], Xchain);
201end
202
203function [Uchain, Qchain, Wchain, Tchain, statName] = parse_dollar_output(strline, Uchain, Qchain, Wchain, Tchain)
204statName = {};
205if any(find(strline==','))
206 if any(find(strline=='$'))
207 % skip
208 else
209 R = size(Uchain,2);
210 %strline
211 str=strrep(strline,' ','');
212 str=strsplit(str,',');
213 Uchain(end+1,1:R) = 0;
214 Qchain(end+1,1:R) = 0;
215 Wchain(end+1,1:R) = 0;
216 Tchain(end+1,1:R) = 0;
217 ptr = 1;
218 statName{end+1} = str{1};
219 for r=1:R
220 Qchain(end,r)=str2num(str{ptr+r});
221 end
222 ptr = ptr + 1 + R; % skip aggregate value
223 for r=1:R
224 Wchain(end,r)=str2num(str{ptr+r});
225 end
226 ptr = ptr + 1 + R; % skip aggregate value
227 for r=1:R
228 Uchain(end,r)=str2num(str{ptr+r});
229 end
230 ptr = ptr + 1 + R; % skip aggregate value
231 for r=1:R
232 Tchain(end,r)=str2num(str{ptr+r});
233 end
234 end
235end
236%Station, $Q(Chain01), $Q(Chain02), $Q, $R(Chain01), $R(Chain02), $R, $U(Chain01), $U(Chain02), $U, $X(Chain01), $X(Chain02), $X
237%Delay1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2.63033, 7.40631, 10.0366
238%Queue1, 4, 4, 8, 1.52072, 0.54008, 0.797079, 1, 1, 2, 2.63033, 7.40631, 10.0366
239end
240
241function [Uchain, Qchain, Wchain, Tchain, statName] = parse_dollar_output_singleclass(strline, Uchain, Qchain, Wchain, Tchain)
242statName = {};
243if any(find(strline==','))
244 if any(find(strline=='$'))
245 % skip
246 else
247 R = size(Uchain,2);
248 %strline
249 str=strrep(strline,' ','');
250 str=strsplit(str,',');
251 Uchain(end+1,1:R) = 0;
252 Qchain(end+1,1:R) = 0;
253 Wchain(end+1,1:R) = 0;
254 Tchain(end+1,1:R) = 0;
255 ptr = 1;
256 statName{end+1} = str{1};
257 for r=1:R
258 Qchain(end,r)=str2num(str{ptr+r});
259 end
260 ptr = ptr + 1;
261 for r=1:R
262 Wchain(end,r)=str2num(str{ptr+r});
263 end
264 ptr = ptr + 1;
265 for r=1:R
266 Uchain(end,r)=str2num(str{ptr+r});
267 end
268 ptr = ptr + 1;
269 for r=1:R
270 Tchain(end,r)=str2num(str{ptr+r});
271 end
272 end
273end
274%Station, $Q(Chain01), $Q(Chain02), $Q, $R(Chain01), $R(Chain02), $R, $U(Chain01), $U(Chain02), $U, $X(Chain01), $X(Chain02), $X
275%Delay1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2.63033, 7.40631, 10.0366
276%Queue1, 4, 4, 8, 1.52072, 0.54008, 0.797079, 1, 1, 2, 2.63033, 7.40631, 10.0366
277end
278
279function [Lchain, Uchain, Qchain, Wchain, Tchain] = parse_starred_output(strline, Lchain, Uchain, Qchain, Wchain, Tchain)
280if any(find(strline=='.'))
281 str=strline(2:end);
282 str=strrep(str,' ','');
283 %[name,service,busypct,custnb,response,thruput]
284 str=strsplit(str,'*');
285 name = str{1};
286 if name(1)=='('
287 chainnum = strrep(name,'(Chain','');
288 chainnum = str2num(strrep(chainnum,')',''));
289 Lchain(i,chainnum) = str2num(str{2});
290 Uchain(i,chainnum) = str2num(str{3});
291 Qchain(i,chainnum) = str2num(str{4});
292 Wchain(i,chainnum) = str2num(str{5});
293 Tchain(i,chainnum) = str2num(str{6});
294 else
295 i=find(cellfun(@any,strfind(stationames,name)));
296 end
297end
298end
Definition mmt.m:124