1function [QN,UN,RN,TN,CN,XN,actualMethod] = solver_qns(sn, options)
2% [Q,U,R,T,C,X] = SOLVER_QNS(QN, OPTIONS)
4% Copyright (c) 2012-2026, Imperial College London
7M = sn.nstations; % number of stations
8K = sn.nclasses; % number of classes
17filePath = lineTempName(
'qns');
19fname = [fileName,
'.jmva'];
20logFileName =
'console';
21logfname = [logFileName,
'.out'];
22outputFileName = [filePath,filesep,fname];
23outputFileName = SolverJMT.writeJMVA(sn, outputFileName, options);
25ofname = [fileName,
'.jmva'];
26resultFileName = [filePath,filesep,ofname];
27%stationames={sn.nodenames{sn.isstation}};
29% Track the actual method that will be used
30actualMethod = options.method;
32line_debug(
'QNS solver starting: nstations=%d, nclasses=%d, method=%s', M, K, options.method);
36 options.config.multiserver=
'conway';
38 options.config.multiserver=
'reiser';
40 options.config.multiserver=
'rolia';
42 options.config.multiserver=
'zhou';
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');
51 line_debug('Using Conway multiserver approximation');
53 cmd=['qnsolver -l ',outputFileName,' -mconway -o ',resultFileName,' > ',logfname];
55 cmd=['qnsolver -l ',outputFileName,' -mconway -o ',resultFileName,' > ',logfname,' 2>&1'];
57 actualMethod = 'conway';
59 line_debug('Using Reiser multiserver approximation');
61 cmd=['qnsolver -l ',outputFileName,' -mreiser -o ',resultFileName,' > ',logfname];
63 cmd=['qnsolver -l ',outputFileName,' -mreiser -o ',resultFileName,' > ',logfname,' 2>&1'];
65 actualMethod = 'reiser';
67 line_debug('Using Rolia multiserver approximation');
69 cmd=['qnsolver -l ',outputFileName,' -mrolia -o ',resultFileName,' > ',logfname];
71 cmd=['qnsolver -l ',outputFileName,' -mrolia -o ',resultFileName,' > ',logfname,' 2>&1'];
73 actualMethod = 'rolia';
75 line_debug('Using Zhou multiserver approximation');
77 cmd=['qnsolver -l ',outputFileName,' -mzhou -o ',resultFileName,' > ',logfname];
79 cmd=['qnsolver -l ',outputFileName,' -mzhou -o ',resultFileName,' > ',logfname,' 2>&1'];
81 actualMethod = 'zhou';
85 cmd=['qnsolver -l ',outputFileName,' -o ',resultFileName,' > ',logfname];
87 cmd=['qnsolver -l ',outputFileName,' -o ',resultFileName,' > ',logfname,' 2>&1'];
91% see _kb/06-solver-catalog.md (Wrappers: LQNS/lqsim/qnsolver LD_LIBRARY_PATH GLIBCXX strip)
93 cmd = ['env -u LD_LIBRARY_PATH ', cmd];
95if GlobalConstants.Verbose == VerboseLevel.DEBUG
96 line_printf('SolverQNS command:\n');
100name = cell(sn.nstations*(sn.nchains+1),0);
101Uchain = zeros(0,sn.nchains);
102Qchain = zeros(0,sn.nchains);
103Wchain = zeros(0,sn.nchains);
104Tchain = zeros(0,sn.nchains);
105[Lchain,STchain,Vchain,alpha,~,~,~] = sn_get_demands_chain(sn);
106%Lchain = zeros(sn.nstations,sn.nchains); % uncomment for starred output
110 fid=fopen(resultFileName,
'r');
113 strline = fgetl(fid);
115 [Uchain, Qchain, Wchain, Tchain, statlabel] = parse_dollar_output_singleclass(strline, Uchain, Qchain, Wchain, Tchain);
117 [Uchain, Qchain, Wchain, Tchain, statlabel] = parse_dollar_output(strline, Uchain, Qchain, Wchain, Tchain);
119 if ~isempty(statlabel)
120 statName{end+1} = statlabel;
122 %[Lchain, Uchain, Qchain, Wchain, Tchain] = parse_starred_output(strline, Lchain, Uchain, Qchain, Wchain, Tchain);
126 line_warning(mfilename,
'Failed execution: cannot open the qnsolver output file at: ');
127 line_warning(mfilename,resultFileName)
137% Build reorder mapping: maps model station index to qnsolver output row
138% Initialize with zeros (will remain 0
for stations not found in qnsolver output)
139stationToOutputRow = zeros(1, sn.nnodes);
140for i=1:length(statName)
141 idx = find(cellfun(@(x)strcmp(x,statName{i}),sn.nodenames));
143 stationToOutputRow(idx) = i;
147% Create reordered arrays
for stations only
148% Stations are the first sn.nstations
nodes
149UchainReordered = zeros(sn.nstations, sn.nchains);
150QchainReordered = zeros(sn.nstations, sn.nchains);
151WchainReordered = zeros(sn.nstations, sn.nchains);
152TchainReordered = zeros(sn.nstations, sn.nchains);
155 outputRow = stationToOutputRow(i);
157 UchainReordered(i,:) = Uchain(outputRow,:);
158 QchainReordered(i,:) = Qchain(outputRow,:);
159 WchainReordered(i,:) = Wchain(outputRow,:);
160 TchainReordered(i,:) = Tchain(outputRow,:);
164Uchain = UchainReordered;
165Qchain = QchainReordered;
166Wchain = WchainReordered;
167Tchain = TchainReordered;
169ref= zeros(sn.nchains,1);
171 chain = find(sn.chains(c,:));
172 Xchain(c)=Tchain(sn.refstat(c),c);
174 % For open chains where refstat
is Source (not in qnsolver output),
175 % recover Xchain from any station with valid throughput:
176 % Xchain(c) = Tchain(i,c) / Vchain(i,c)
178 if Vchain(i,c) > 0 && Tchain(i,c) > 0
179 Xchain(c) = Tchain(i,c) / Vchain(i,c);
185%Rchain=Wchain./Vchain; % needs to be reinstated
for starred output
187Rchain(isnan(Rchain))=0;
189 if ~isinf(sn.nservers(i))
190 Uchain(i,:) = Uchain(i,:) / sn.nservers(i);
194[QN,UN,RN,TN,CN,XN] = sn_deaggregate_chain_results(sn, Lchain, [], STchain, Vchain, alpha, Qchain, Uchain, Rchain, Tchain, [], Xchain);
197function [Uchain, Qchain, Wchain, Tchain, statName] = parse_dollar_output(strline, Uchain, Qchain, Wchain, Tchain)
199if any(find(strline==
','))
200 if any(find(strline=='$'))
205 str=strrep(strline,' ','');
206 str=strsplit(str,',');
207 Uchain(end+1,1:R) = 0;
208 Qchain(end+1,1:R) = 0;
209 Wchain(end+1,1:R) = 0;
210 Tchain(end+1,1:R) = 0;
212 statName{end+1} = str{1};
214 Qchain(end,r)=str2num(str{ptr+r});
216 ptr = ptr + 1 + R; % skip aggregate value
218 Wchain(end,r)=str2num(str{ptr+r});
220 ptr = ptr + 1 + R; % skip aggregate value
222 Uchain(end,r)=str2num(str{ptr+r});
224 ptr = ptr + 1 + R; % skip aggregate value
226 Tchain(end,r)=str2num(str{ptr+r});
230%
Station, $Q(Chain01), $Q(Chain02), $Q, $R(Chain01), $R(Chain02), $R, $U(Chain01), $U(Chain02), $U, $X(Chain01), $X(Chain02), $X
231%Delay1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2.63033, 7.40631, 10.0366
232%Queue1, 4, 4, 8, 1.52072, 0.54008, 0.797079, 1, 1, 2, 2.63033, 7.40631, 10.0366
235function [Uchain, Qchain, Wchain, Tchain, statName] = parse_dollar_output_singleclass(strline, Uchain, Qchain, Wchain, Tchain)
237if any(find(strline==
','))
238 if any(find(strline=='$'))
243 str=strrep(strline,' ','');
244 str=strsplit(str,',');
245 Uchain(end+1,1:R) = 0;
246 Qchain(end+1,1:R) = 0;
247 Wchain(end+1,1:R) = 0;
248 Tchain(end+1,1:R) = 0;
250 statName{end+1} = str{1};
252 Qchain(end,r)=str2num(str{ptr+r});
256 Wchain(end,r)=str2num(str{ptr+r});
260 Uchain(end,r)=str2num(str{ptr+r});
264 Tchain(end,r)=str2num(str{ptr+r});
268%
Station, $Q(Chain01), $Q(Chain02), $Q, $R(Chain01), $R(Chain02), $R, $U(Chain01), $U(Chain02), $U, $X(Chain01), $X(Chain02), $X
269%Delay1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2.63033, 7.40631, 10.0366
270%Queue1, 4, 4, 8, 1.52072, 0.54008, 0.797079, 1, 1, 2, 2.63033, 7.40631, 10.0366
273function [Lchain, Uchain, Qchain, Wchain, Tchain] = parse_starred_output(strline, Lchain, Uchain, Qchain, Wchain, Tchain)
274if any(find(strline==
'.'))
276 str=strrep(str,' ','');
277 %[name,service,busypct,custnb,response,thruput]
278 str=strsplit(str,'*');
281 chainnum = strrep(name,
'(Chain',
'');
282 chainnum = str2num(strrep(chainnum,
')',
''));
283 Lchain(i,chainnum) = str2num(str{2});
284 Uchain(i,chainnum) = str2num(str{3});
285 Qchain(i,chainnum) = str2num(str{4});
286 Wchain(i,chainnum) = str2num(str{5});
287 Tchain(i,chainnum) = str2num(str{6});
289 i=find(cellfun(@any,strfind(stationames,name)));