LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_mam.m
1function [QN,UN,RN,TN,CN,XN,totiter,method,runtime] = solver_mam(sn, options)
2%[Q,U,R,T,C,X,totiter] = SOLVER_MAM(QN, PH, OPTIONS)
3
4%Copyright (c) 2012-2026, Imperial College London
5%All rights reserved.
6
7method = options.method;
8config = options.config;
9totiter = NaN;
10PH = sn.proc;
11I = sn.nnodes;
12M = sn.nstations;
13K = sn.nclasses;
14C = sn.nchains;
15N = sn.njobs';
16V = cellsum(sn.visits);
17Tstart=tic;
18QN = zeros(M,K);
19UN = zeros(M,K);
20RN = zeros(M,K);
21TN = zeros(M,K);
22CN = zeros(1,K);
23XN = zeros(1,K);
24
25lambda = zeros(1,K);
26for c=1:C
27 inchain = sn.inchain{c};
28 lambdas_inchain = sn.rates(sn.refstat(inchain(1)),inchain);
29 lambdas_inchain = lambdas_inchain(isfinite(lambdas_inchain));
30 lambda(inchain) = sum(lambdas_inchain);
31end
32
33chain = zeros(1,K);
34for k=1:K
35 chain(k) = find(sn.chains(:,k));
36end
37
38for ist=1:sn.nstations
39 switch sn.sched(ist)
40 case SchedStrategy.EXT
41 % no-op
42 case {SchedStrategy.FCFS, SchedStrategy.HOL, SchedStrategy.FCFSPRPRIO, SchedStrategy.PS}
43 % no-op
44 otherwise
45 if options.verbose
46 line_warning(mfilename,'The dec.mmap method does not support this scheduling strategy.\n');
47 end
48 [QN,UN,RN,TN,CN,XN] = deal([],[],[],[],[],[]);
49 totiter = 0;
50 method = '';
51 runtime = toc(Tstart);
52 return
53 end
54end
55
56if all(isinf(sn.njobs)) % is open
57 % open queueing system (one node is the external world)
58 pie = {};
59 D0 = {};
60 for ist=1:M
61 switch sn.sched(ist)
62 case SchedStrategy.EXT
63 TN(ist,:) = sn.rates(ist,:);
64 TN(ist,isnan(TN(ist,:)))=0;
65 case {SchedStrategy.FCFS, SchedStrategy.HOL, SchedStrategy.FCFSPRPRIO, SchedStrategy.PS}
66 for k=1:K
67 % divide service time by number of servers and put
68 % later a surrogate delay server in tandem to compensate
69 PH{ist}{k} = map_scale(PH{ist}{k}, map_mean(PH{ist}{k})/sn.nservers(ist));
70 pie{ist}{k} = map_pie(PH{ist}{k});
71 D0{ist,k} = PH{ist}{k}{1};
72 if any(isnan(D0{ist,k}))
73 D0{ist,k} = -GlobalConstants.Immediate;
74 pie{ist}{k} = 1;
75 PH{ist}{k} = map_exponential(GlobalConstants.Immediate);
76 end
77 end
78 end
79 end
80
81 % departure-process fixed point (parametric decomposition), driven on
82 % the station queue lengths by the generic DA driver
83 DEP = {};
84 fpopts = options;
85 fpopts.config.da_miniter = 3; % legacy loop tested convergence only from the third sweep
86 fpopts.config.da_norm = @(xn,xr) max(abs(xn(:)-xr(:))./xr(:)); % relative difference
87 [~, totiter] = da_fpi(@mam_dec_sweep, QN, fpopts);
88 if options.verbose
89 line_printf('\nMAM parametric decomposition completed in %d iterations.',totiter);
90 end
91else
92 if options.verbose
93 line_warning(mfilename,'This model is not supported by SolverMAM yet. Returning with no result.\n');
94 end
95end
96runtime = toc(Tstart);
97
98 function [xnew, xref] = mam_dec_sweep(~, itnum)
99 %it
100 % now estimate arrival processes
101 if itnum == 1
102 % initially form departure processes using scaled service
103 % DEP, PH and V are all STATION-indexed, and solver_mam_traffic
104 % reads DEP{ist,r} likewise. Indexing them by node would only
105 % coincide when every station precedes every non-station node in
106 % node order (e.g. Source,Queue,Sink); with a Sink declared before a
107 % Queue, nodeToStation returns NaN and PH{NaN} errors out.
108 DEP = PH;
109 for ist=1:M
110 for r=1:K
111 DEP{ist,r} = map_scale(PH{ist}{r}, 1 / (lambda(r) * V(ist,r)) );
112 end
113 end
114 end
115
116 ARV = solver_mam_traffic(sn, DEP, config);
117
118 xref = QN;
119 for ist=1:M
120 ind = sn.stationToNode(ist);
121 finiteCapUsed = false;
122 switch sn.nodetype(ind)
123 case NodeType.Queue
124 if length(ARV{ind}{1}) > config.space_max
125 line_printf('\nArrival process at node %d is now at %d states. Compressing.',ind,length(ARV{ind}{1}));
126 ARV{ind} = mmap_compress(ARV{ind});
127 end
128 TN(ist,:) = mmap_lambda(ARV{ind});
129 switch sn.sched(ist)
130 case {SchedStrategy.FCFS, SchedStrategy.HOL, SchedStrategy.FCFSPRPRIO}
131 isFiniteCap = isfinite(sn.cap(ist));
132 if isFiniteCap
133 capK = sn.cap(ist);
134 [isMmck, muMmck] = mam_detect_mmck(sn, ist, K, ARV{ind});
135 if isMmck
136 aggrLambda_ist = sum(TN(ist,:), 'omitnan');
137 exactRes = qsys_mmck(aggrLambda_ist, muMmck, sn.nservers(ist), capK);
138 meanQ_fc = exactRes.meanQueueLength;
139 lossProb_fc = exactRes.lossProbability;
140 else
141 [meanQ_fc, lossProb_fc, ~] = mam_truncate_renorm( ...
142 {ARV{ind}{[1,3:end]}}, {pie{ist}{:}}, {D0{ist,:}}, capK);
143 end
144 lambdaInflow = TN(ist,:); % per-class rates from mmap_lambda
145 lambdaInflow(isnan(lambdaInflow)) = 0;
146 TN_eff = lambdaInflow * (1 - lossProb_fc);
147 sumTN = sum(TN_eff);
148 % Actual per-class service mean (PH was scaled by 1/c)
149 S_actual = zeros(1, K);
150 for k=1:K
151 S_actual(k) = map_mean(PH{ist}{k}) * sn.nservers(ist);
152 end
153 if sumTN > 0
154 Savg_eff = sum(TN_eff .* S_actual, 'omitnan') / sumTN;
155 Wq = max(0, meanQ_fc / sumTN - Savg_eff);
156 else
157 Wq = 0;
158 end
159 for k=1:K
160 TN(ist,k) = TN_eff(k);
161 UN(ist,k) = TN(ist,k) * map_mean(PH{ist}{k});
162 if TN(ist,k) > 0
163 RN(ist,k) = Wq + S_actual(k);
164 QN(ist,k) = TN(ist,k) * RN(ist,k);
165 else
166 RN(ist,k) = 0;
167 QN(ist,k) = 0;
168 end
169 end
170 finiteCapUsed = true;
171 else
172 [Qret{1:K}, ~] = MMAPPH1FCFS({ARV{ind}{[1,3:end]}}, {pie{ist}{:}}, {D0{ist,:}}, 'ncMoms', 1, 'ncDistr',2);
173 for k=1:K
174 QN(ist,k) = sum(Qret{k});
175 end
176 end
177 case SchedStrategy.PS
178 for k=1:K
179 UN(ist,k) = TN(ist,k) * map_mean(PH{ist}{k});
180 end
181 Uden = min([1-GlobalConstants.FineTol, sum(UN(ist,:))]);
182 for k=1:K
183 QN(ist,k) = UN(ist,k)/(1-Uden);
184 end
185 end
186 end
187 if ~finiteCapUsed
188 for k=1:K
189 UN(ist,k) = TN(ist,k) * map_mean(PH{ist}{k});
190 %add number of jobs at the surrogate delay server
191 QN(ist,k) = QN(ist,k) + TN(ist,k)*(map_mean(PH{ist}{k})*sn.nservers(ist)) * (sn.nservers(ist)-1)/sn.nservers(ist);
192 RN(ist,k) = QN(ist,k) ./ TN(ist,k);
193 end
194 end
195 end
196
197 for ist=1:M
198 ind = sn.stationToNode(ist);
199 switch sn.nodetype(ind)
200 case NodeType.Queue
201 for r=1:K
202 % extract class-r arrival MAP
203 A = mmap_hide(ARV{ind},setdiff(1:K,r));
204 S = PH{ist}{r};
205 na = length(A{1});
206 ns = length(S{1});
207 etaqa_n = config.etaqa_trunc;
208 etaqa_sz = (etaqa_n+1)*na*ns;
209 rho = sum(UN(ist,:));
210 % use ETAQA if state space is manageable and queue is stable
211 if etaqa_sz <= config.space_max && rho < 1-GlobalConstants.FineTol
212 try
213 switch sn.sched(ist)
214 case {SchedStrategy.FCFS, SchedStrategy.HOL, SchedStrategy.FCFSPRPRIO}
215 DEP{ist,r} = qbd_depproc_etaqa(A, S, etaqa_n);
216 case SchedStrategy.PS
217 DEP{ist,r} = qbd_depproc_etaqa_ps(A, S, etaqa_n);
218 end
219 DEP{ist,r} = map_normalize(DEP{ist,r});
220 catch
221 % fall back to scaled service on ETAQA failure
222 DEP{ist,r} = PH{ist}{r};
223 end
224 else
225 DEP{ist,r} = PH{ist}{r};
226 end
227 DEP{ist,r} = map_scale(DEP{ist,r}, 1 / (lambda(r) * V(ist,r)) );
228 SCVd(ist,r) = map_scv(DEP{ist,r});
229 IDCd(ist,r) = map_idc(DEP{ist,r});
230 end
231 end
232 end
233 xnew = QN;
234 end
235end