LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_mam_analyzer.m
1function [QN,UN,RN,TN,CN,XN,runtime,method,totiter,percResults] = solver_mam_analyzer(sn, options)
2% [QN,UN,RN,TN,CN,XN,RUNTIME,METHOD] = SOLVER_MAM_ANALYZER(QN, OPTIONS)
3
4% Copyright (c) 2012-2026, Imperial College London
5% All rights reserved.
6
7Tstart = tic;
8
9% Preserve deterministic distributions for exact MAP/D/c analysis
10if ~isfield(options, 'config')
11 options.config = struct();
12end
13if ~isfield(options.config, 'preserveDet')
14 options.config.preserveDet = true; % Enable exact MAP/D/c solver
15end
16
17% Convert non-Markovian distributions to PH (Det preserved if preserveDet=true)
18sn = sn_nonmarkov_toph(sn, options);
19
20if nargin<2 || isempty(options.config) || ~isfield(options.config,'merge')
21 % Preserve existing config fields (like preserveDet) while setting defaults
22 if ~isfield(options, 'config') || isempty(options.config)
23 options.config = struct();
24 end
25 if ~isfield(options.config, 'merge')
26 options.config.merge = 'super';
27 end
28 if ~isfield(options.config, 'compress')
29 options.config.compress = 'mixture.order1';
30 end
31 if ~isfield(options.config, 'space_max')
32 options.config.space_max = 128;
33 end
34end
35
36method = options.method;
37percResults = []; % Initialize as empty
38
39switch options.method
40 case 'dec.mmap'
41 % service distribution per class scaled by utilization used as
42 % departure process
43 line_debug('Using dec.mmap method, calling solver_mam');
44 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam(sn, options);
45 case {'default', 'dec.source'}
46 % Check if network is a valid Fork-Join topology for FJ_codes
47 [isFJ, fjInfo] = fj_isfj(sn);
48
49 if isFJ
50 % Use FJ_codes for Fork-Join analysis
51 if strcmpi(options.method, 'default')
52 line_debug('Default method: using FJ_codes for Fork-Join topology\n');
53 end
54 line_debug('Detected Fork-Join topology, using FJ_codes method');
55 [QN,UN,RN,TN,CN,XN,totiter,percResults] = solver_mam_fj(sn, options);
56 method = 'qiu';
57 else
58 % Check if network is a valid BMAP/PH/N/N bufferless retrial queue
59 [isRetrial, ~] = qsys_is_retrial(sn);
60
61 % Check if network has reneging (queue abandonment) for MAPMSG
62 isReneging = hasRenegingPatience(sn);
63
64 if isRetrial
65 % Use BMAP/PH/N/N retrial solver
66 if strcmpi(options.method, 'default')
67 line_debug('Default method: using retrial for BMAP/PH/N/N bufferless topology\n');
68 end
69 line_debug('Detected BMAP/PH/N/N retrial topology, using retrial method');
70 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam_retrial(sn, options);
71 method = 'retrial';
72 elseif isReneging
73 % Use MAP/M/s+G reneging solver (MAPMSG)
74 if strcmpi(options.method, 'default')
75 line_debug('Default method: using MAPMSG for MAP/M/s+G with reneging\n');
76 end
77 line_debug('Detected reneging topology, using MAPMSG method');
78 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam_retrial(sn, options);
79 method = 'reneging';
80 else
81 % arrival process per chain rescaled by visits at each node
82 if strcmpi(options.method, 'default')
83 line_debug('Default method: using dec.source\n');
84 end
85 line_debug('Using default/dec.source method, calling solver_mam_basic');
86 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam_basic(sn, options);
87 method = 'dec.source';
88 end
89 end
90 case 'dec.poisson'
91 % analyze the network with Poisson streams
92 line_debug('Using dec.poisson method with space_max=1, calling solver_mam_basic');
93 options.config.space_max = 1;
94 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam_basic(sn, options);
95 case 'mna'
96 if sn_is_open_model(sn)
97 line_debug('Using MNA method for open model, calling solver_mna_open');
98 [QN,UN,RN,TN,CN,XN,~,totiter] = solver_mna_open(sn, options);
99 elseif sn_is_closed_model(sn)
100 line_debug('Using MNA method for closed model, calling solver_mna_closed');
101 [QN,UN,RN,TN,CN,XN,~,totiter] = solver_mna_closed(sn, options);
102 else
103 line_error(mfilename,'The mna method in SolverMAM does not support mixed models.');
104 end
105 case 'ldqbd'
106 % Level-Dependent QBD method for single-class closed networks
107 if ~sn_is_closed_model(sn)
108 line_error(mfilename,'The ldqbd method requires a closed model.');
109 end
110 if sn.nclasses ~= 1
111 line_error(mfilename,'The ldqbd method requires a single-class model.');
112 end
113 line_debug('Using LDQBD method for closed model, calling solver_mam_ldqbd');
114 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam_ldqbd(sn, options);
115 case {'inap', 'inapplus', 'exact'}
116 % RCAT-based methods (formerly SolverAG)
117 line_debug('Using RCAT method: %s', options.method);
118 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam_ag(sn, options);
119 otherwise
120 line_error(mfilename,'Unknown method: %s', options.method);
121end
122
123for i=1:sn.nstations
124 switch sn.sched(i)
125 case SchedStrategy.EXT
126 TN(i,:) = sn.rates(i,:);
127 end
128end
129
130QN(isnan(QN))=0;
131CN(isnan(CN))=0;
132RN(isnan(RN))=0;
133UN(isnan(UN))=0;
134XN(isnan(XN))=0;
135TN(isnan(TN))=0;
136
137runtime = toc(Tstart);
138end
139
140function isReneging = hasRenegingPatience(sn)
141% HASRENEGINGPATIENCE Check if model has reneging/patience configured
142%
143% Returns true if any queue station has reneging (ImpatienceType.RENEGING)
144% configured with a patience distribution.
145
146isReneging = false;
147
148% Check if impatienceClass field exists (ImpatienceType: RENEGING, BALKING)
149if ~isfield(sn, 'impatienceClass') || isempty(sn.impatienceClass)
150 return;
151end
152
153% Check if patienceProc field exists
154if ~isfield(sn, 'patienceProc') || isempty(sn.patienceProc)
155 return;
156end
157
158% Check each station for reneging configuration
159for ist = 1:sn.nstations
160 for r = 1:sn.nclasses
161 if sn.impatienceClass(ist, r) == ImpatienceType.RENEGING
162 if ~isempty(sn.patienceProc{ist, r})
163 isReneging = true;
164 return;
165 end
166 end
167 end
168end
169
170end