1function [Q,U,R,T,C,X,lG,runtime,iter,method] = solver_nc_analyzer(sn, options)
2% [Q,U,R,T,C,X,LG,RUNTIME,METHOD] = SOLVER_NC_ANALYZER(QN, OPTIONS)
4% Copyright (c) 2012-2026, Imperial College London
8line_debug(
'NC analyzer starting: method=%s, nstations=%d, nclasses=%d, njobs=%s', options.method, sn.nstations, sn.nclasses, mat2str(sn.njobs));
10% Order-independent (OI) closed network:
explicit request, or
auto-detected
11% when the closed model consists solely of OI and delay stations. Solved
12% exactly by the balanced-fairness normalizing constant (pfqn_ncoi) with the
13% OI functional-server (pfqn_oi_fnc) identity
for the mean queue lengths.
14if nc_is_oi_model(sn) && any(strcmpi(options.method, {
'default',
'exact'}))
15 line_debug('NC analyzer routing to solver_nc_oi_analyzer (order-independent)');
16 [Q,U,R,T,C,X,lG,runtime,iter,method] = solver_nc_oi_analyzer(sn, options);
20% IS specialized to OI/
P&S stations -> pfqn_pas_is (reduces to pfqn_oi_is for an
21% empty swap graph); see _kb/06-solver-catalog.md (NC section, analyzer routing)
22if nc_is_pas_model(sn) && any(strcmpi(options.method, {
'default',
'is',
'sampling'}))
23 line_debug('NC analyzer routing to solver_nc_pas_is_analyzer (pass-and-swap IS)');
24 [Q,U,R,T,C,X,lG,runtime,iter,method] = solver_nc_pas_is_analyzer(sn, options);
28% plain '
is'
is the sample-an-ordering family for closed product-
form networks;
29% no open-class
form. see _kb/06-solver-catalog.md (NC section, analyzer routing)
30if strcmpi(options.method, '
is') && any(isinf(sn.njobs))
31 line_error(mfilename, 'The ''
is'' importance-sampling method requires a closed queueing network. Use ''sampling'' (pfqn_mci/pfqn_ls) for open or mixed models.');
34% Maximum Entropy Method (Kouvatsos 1994): explicit request only. The
35% method='default' path routes to the native normalizing-constant analyzer
36% (solver_nc), as it did before MEM was introduced.
37if strcmpi(options.method, 'mem')
38 line_debug('NC analyzer routing to solver_nc_mem (Maximum Entropy)');
39 [Q,U,R,T,C,X,iter,method] = solver_nc_mem(sn, options);
41 runtime = toc(Tstart);
45nservers = sn.nservers;
46if max(nservers(nservers<Inf))>1 & any(isinf(sn.njobs)) & strcmpi(options.method,'exact') %
#ok<AND2>
47 line_error(mfilename,
'NC solver cannot provide exact solutions for open or mixed queueing networks. Remove the ''exact'' option.');
50% interpolate
for non-integer closed populations
51eta = abs(sn.njobs - floor(sn.njobs));
52if any(eta>GlobalConstants.FineTol)
53 line_debug('Fractional populations detected, using interpolation');
54 sn_floor = sn; sn_floor.njobs = floor(sn.njobs);
55 [Qf,Uf,Rf,Tf,Cf,Xf,lGf,~,iterf] = solver_nc(sn_floor, options);
56 sn_ceil = sn; sn_ceil.njobs = ceil(sn.njobs);
57 [Qc,Uc,Rc,Tc,Cc,Xc,lGc,~,iterc,method] = solver_nc(sn_ceil, options);
58 Q = Qf + eta .* (Qc-Qf);
59 U = Uf + eta .* (Uc-Uf);
60 R = Rf + eta .* (Rc-Rf);
61 T = Tf + eta .* (Tc-Tf);
62 C = Cf + eta .* (Cc-Cf);
63 X = Xf + eta .* (Xc-Xf);
64 lG = lGf + eta .* (lGf-lGc);
66 line_debug('NC interpolation complete: used %d + %d iterations', iterf, iterc);
67else % if integers or open model
68 if any(isinf(sn.njobs))
69 line_debug('Open/mixed model detected, calling solver_nc');
71 line_debug('Using exact integer populations, calling solver_nc');
73 [Q,U,R,T,C,X,lG,~,iter,method] = solver_nc(sn, options);