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
20% Check if the model is mixed (has both open and closed classes)
21isOpen = sn_is_open_model(sn);
22isClosed = sn_is_closed_model(sn);
23isMixed = isOpen && isClosed;
24
25line_debug('MAM analyzer starting: method=%s, isOpen=%d, isClosed=%d', options.method, isOpen, isClosed);
26
27% Mixed models are supported by the dec.source method
28
29if nargin<2 || isempty(options.config) || ~isfield(options.config,'merge')
30 % Preserve existing config fields (like preserveDet) while setting defaults
31 if ~isfield(options, 'config') || isempty(options.config)
32 options.config = struct();
33 end
34 if ~isfield(options.config, 'merge')
35 options.config.merge = 'super';
36 end
37 if ~isfield(options.config, 'compress')
38 options.config.compress = 'mixture.order1';
39 end
40 if ~isfield(options.config, 'space_max')
41 options.config.space_max = 128;
42 end
43 if ~isfield(options.config, 'etaqa_trunc')
44 options.config.etaqa_trunc = 8;
45 end
46end
47
48method = options.method;
49percResults = []; % Initialize as empty
50
51switch options.method
52 case 'dec.mmap'
53 % service distribution per class scaled by utilization used as
54 % departure process
55 line_debug('Using dec.mmap method, calling solver_mam');
56 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam(sn, options);
57 case {'default', 'dec.source'}
58 % Check if network is a valid Fork-Join topology for FJ_codes
59 [isFJ, fjInfo] = fj_isfj(sn);
60
61 if isFJ
62 % Use FJ_codes for Fork-Join analysis
63 if strcmpi(options.method, 'default')
64 line_debug('Default method: using FJ_codes for Fork-Join topology\n');
65 end
66 line_debug('Detected Fork-Join topology, using FJ_codes method');
67 [QN,UN,RN,TN,CN,XN,totiter,percResults] = solver_mam_fj(sn, options);
68 method = 'qiu';
69 elseif sn_has_fork_join(sn) && sn_is_open_model(sn)
70 % Use MMAP-based FJ decomposition with mmap_max synchronization
71 line_debug('Detected general Fork-Join topology, using dec.source.fj method');
72 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam_basic_fj(sn, options);
73 method = 'dec.source.fj';
74 else
75 % Check if network is a valid BMAP/PH/N/N bufferless retrial queue
76 [isRetrial, ~] = qsys_is_retrial(sn);
77
78 % Check if network has reneging (queue abandonment) for MAPMsG
79 isReneging = hasRenegingPatience(sn);
80
81 if isRetrial
82 % Use BMAP/PH/N/N retrial solver
83 if strcmpi(options.method, 'default')
84 line_debug('Default method: using retrial for BMAP/PH/N/N bufferless topology\n');
85 end
86 line_debug('Detected BMAP/PH/N/N retrial topology, using retrial method');
87 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam_retrial(sn, options);
88 method = 'retrial';
89 elseif isReneging
90 % Use MAP/M/s+G reneging solver (MAPMsG)
91 if strcmpi(options.method, 'default')
92 line_debug('Default method: using MAPMsG for MAP/M/s+G with reneging\n');
93 end
94 line_debug('Detected reneging topology, using MAPMsG method');
95 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam_retrial(sn, options);
96 method = 'reneging';
97 else
98 % arrival process per chain rescaled by visits at each node
99 if strcmpi(options.method, 'default')
100 line_debug('Default method: using dec.source\n');
101 end
102 line_debug('Using default/dec.source method, calling solver_mam_basic');
103 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam_basic(sn, options);
104 method = 'dec.source';
105 end
106 end
107 case 'dec.poisson'
108 % analyze the network with Poisson streams
109 line_debug('Using dec.poisson method with space_max=1, calling solver_mam_basic');
110 options.config.space_max = 1;
111 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam_basic(sn, options);
112 case 'mna'
113 if sn_is_open_model(sn)
114 line_debug('Using MNA method for open model, calling solver_mna_open');
115 [QN,UN,RN,TN,CN,XN,~,totiter] = solver_mna_open(sn, options);
116 elseif sn_is_closed_model(sn)
117 line_debug('Using MNA method for closed model, calling solver_mna_closed');
118 [QN,UN,RN,TN,CN,XN,~,totiter] = solver_mna_closed(sn, options);
119 else
120 line_error(mfilename,'The mna method in SolverMAM does not support mixed models.');
121 end
122 case 'ldqbd'
123 % Level-Dependent QBD method for single-class closed networks
124 if ~sn_is_closed_model(sn)
125 line_error(mfilename,'The ldqbd method requires a closed model.');
126 end
127 if sn.nclasses ~= 1
128 line_error(mfilename,'The ldqbd method requires a single-class model.');
129 end
130 line_debug('Using LDQBD method for closed model, calling solver_mam_ldqbd');
131 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam_ldqbd(sn, options);
132 case {'inap', 'inapplus', 'exact'}
133 % RCAT-based methods (formerly SolverAG)
134 line_debug('Using RCAT method: %s', options.method);
135 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam_ag(sn, options);
136 case 'dec.source.fj'
137 % MMAP-based FJ decomposition with mmap_max synchronization
138 line_debug('Using dec.source.fj method, calling solver_mam_basic_fj');
139 [QN,UN,RN,TN,CN,XN,totiter] = solver_mam_basic_fj(sn, options);
140 otherwise
141 line_error(mfilename,'Unknown method: %s', options.method);
142end
143
144for i=1:sn.nstations
145 switch sn.sched(i)
146 case SchedStrategy.EXT
147 TN(i,:) = sn.rates(i,:);
148 end
149end
150
151QN(isnan(QN))=0;
152CN(isnan(CN))=0;
153RN(isnan(RN))=0;
154UN(isnan(UN))=0;
155XN(isnan(XN))=0;
156TN(isnan(TN))=0;
157
158runtime = toc(Tstart);
159end
160
161function isReneging = hasRenegingPatience(sn)
162% HASRENEGINGPATIENCE Check if model has reneging/patience configured
163%
164% Returns true if any queue station has reneging (ImpatienceType.RENEGING)
165% configured with a patience distribution.
166
167isReneging = false;
168
169% Check if impatienceClass field exists (ImpatienceType: RENEGING, BALKING)
170if ~isfield(sn, 'impatienceClass') || isempty(sn.impatienceClass)
171 return;
172end
173
174% Check if patienceProc field exists
175if ~isfield(sn, 'patienceProc') || isempty(sn.patienceProc)
176 return;
177end
178
179% Check each station for reneging configuration
180for ist = 1:sn.nstations
181 for r = 1:sn.nclasses
182 if sn.impatienceClass(ist, r) == ImpatienceType.RENEGING
183 if ~isempty(sn.patienceProc{ist, r})
184 isReneging = true;
185 return;
186 end
187 end
188 end
189end
190
191end