LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pfqn_schmidt.m
1%{
2%{
3 % @file pfqn_schmidt.m
4 % @brief Schmidt's exact MVA for networks with general scheduling disciplines.
5%}
6%}
7
8%{
9%{
10 % @brief Schmidt's exact MVA for networks with general scheduling disciplines.
11 % @fn pfqn_schmidt(D, N, S, sched, v)
12 % @param D Service demand matrix.
13 % @param N Population vector.
14 % @param S Number of servers per station (matrix or vector).
15 % @param sched Scheduling discipline per station.
16 % @param v Visit ratio matrix (optional, defaults to ones).
17 % @return XN System throughput.
18 % @return QN Mean queue lengths.
19 % @return UN Utilization (M x R), per station-class, D*X/nservers.
20 % @return CN Cycle times.
21 % @return T Results table.
22%}
23%}
24function [XN,QN,UN,CN,T] = pfqn_schmidt(D,N,S,sched,v)
25% [XN,QN,UN,CN] = PFQN_SCHMIDT(D,N,S,SCHED,V)
26
27[M,R] = size(D);
28closedClasses = 1:R;
29XN = zeros(1,R);
30UN = zeros(M,R);
31CN = zeros(M,R);
32QN = zeros(M,R);
33
34% Default visit ratios to ones if not provided
35if nargin < 5 || isempty(v)
36 v = ones(M,R);
37end
38
39% Compute pure service times from demands: S_pure = D / v
40% D is demands (= visit_ratio * service_time), so S_pure = D / v
41% Bcn calculations need pure service times, not demands
42S_pure = D ./ max(v, 1e-12); % Protect against division by zero
43
44C = length(closedClasses); % number of closed classes
45Dc = D(:,closedClasses);
46Nc = N(closedClasses);
47prods = zeros(1,C); % needed for fast hashing
48for r=1:C
49 prods(r)=prod(Nc(1:r-1)+1);
50end
51% Start at nc=(0,...,0)
52kvec = pprod(Nc);
53% Initialize L and Pc
54L = cell(1, M); % mean queue-length
55Pc = cell(1, M); % state probabilities (empty cells for stations that don't need them)
56for ist=1:M
57 % Always initialize L for ALL stations (queue lengths needed everywhere)
58 L{ist} = zeros(R, prod(1+Nc)); % mean queue-length
59 switch sched(ist)
60 case SchedStrategy.INF
61 % No Pc needed for infinite server
62 case SchedStrategy.PS
63 if ~all(S(ist,:) == 1)
64 % Multi-server PS needs state probabilities
65 Pc{ist} = zeros(1 + sum(Nc), prod(1+Nc)); % Pr(j|N)
66 end
67 case SchedStrategy.FCFS
68 if all(D(ist,:)==D(ist,1)) % class-independent
69 if ~all(S(ist,:) == 1) % multi server
70 Pc{ist} = zeros(1 + sum(Nc), prod(1+Nc)); % Pr(j|N)
71 end
72 else % class-dependent - needs vector probabilities
73 Pc{ist} = zeros(prod(1+Nc), prod(1+Nc)); % Pr(jvec|N)
74 end
75 end
76end
77% Per-station throughput like Java: x(ist,c,hkvec) = v(ist,c) * kvec(c) / denom
78x = zeros(M,C,prod(1+Nc));
79w = zeros(M,C,prod(1+Nc));
80for ist=1:M
81 if ~isempty(Pc{ist})
82 Pc{ist}(1 + 0, hashpop(kvec,Nc,C,prods)) = 1.0; %Pj(0|0) = 1
83 end
84end
85% Population recursion
86while all(kvec>=0) && all(kvec <= Nc)
87 nc = sum(kvec);
88 kprods = zeros(1,C); % needed for fast hashing
89 for r=1:C
90 kprods(r)=prod(kvec(1:r-1)+1);
91 end
92 for ist=1:M
93 for c=1:C
94 hkvec = hashpop(kvec,Nc,C,prods);
95 hkvec_c = hashpop(oner(kvec,c),Nc,C,prods);
96 if size(S(ist,:)) == 1
97 ns = S(ist);
98 else
99 ns = S(ist,c);
100 end
101 if kvec(c) > 0
102 switch sched(ist)
103 case SchedStrategy.INF
104 w(ist,c,hkvec) = D(ist,c);
105 case SchedStrategy.PS
106 if ns == 1
107 % Sum queue lengths of ALL classes (arrival theorem)
108 totalQueueLength = sum(L{ist}(:, hkvec_c));
109 w(ist,c,hkvec) = Dc(ist,c) * (1 + totalQueueLength);
110 else
111 % Sum queue lengths of ALL classes (arrival theorem)
112 totalQueueLength = sum(L{ist}(:, hkvec_c));
113 w(ist,c,hkvec) = (Dc(ist,c) / ns) * (1 + totalQueueLength);
114 for j=1:ns-1
115 % Pc{ist}(j,...) = Pr(j-1 jobs) due to MATLAB 1-indexing
116 w(ist,c,hkvec) = w(ist,c,hkvec) + (ns-1-(j-1))*Pc{ist}(j, hkvec_c) * (Dc(ist,c) / ns);
117 end
118 end
119 case SchedStrategy.FCFS
120 if all(D(ist,:)==D(ist,1)) % product-form case
121 if ns == 1
122 % Sum queue lengths of ALL classes (arrival theorem)
123 totalQueueLength = sum(L{ist}(:, hkvec_c));
124 w(ist,c,hkvec) = Dc(ist,c) * (1 + totalQueueLength);
125 else
126 % Sum queue lengths of ALL classes (arrival theorem)
127 totalQueueLength = sum(L{ist}(:, hkvec_c));
128 w(ist,c,hkvec) = (Dc(ist,c) / ns) * (1 + totalQueueLength);
129 for j=1:ns-1
130 % Pc{ist}(j,...) = Pr(j-1 jobs) due to MATLAB 1-indexing
131 w(ist,c,hkvec) = w(ist,c,hkvec) + (ns-1-(j-1))*Pc{ist}(j, hkvec_c) * (Dc(ist,c) / ns);
132 end
133 end
134 else
135 if ns == 1
136 % Sum queue lengths of ALL classes (arrival theorem)
137 totalQueueLength = sum(L{ist}(:, hkvec_c));
138 w(ist,c,hkvec) = Dc(ist,c) * (1 + totalQueueLength);
139 else
140 % Multi-server FCFS class-dependent case
141 nvec = pprod(kvec);
142 while nvec >= 0
143 if nvec(c) > 0
144 % Use Nc and prods for hash (matches Java)
145 hnvec_c = hashpop(oner(nvec,c),Nc,C,prods);
146 % Bcn calculation for multi-server using pure service times
147 if sum(nvec) <= ns
148 Bcn = S_pure(ist,c);
149 else
150 % Weighted average service time based on queue composition
151 % Use epsilon protection for division (matches Java)
152 Bcn = S_pure(ist,c) + max(0,sum(nvec)-ns)/max(ns*(sum(nvec)-1), 1e-12) * (nvec*S_pure(ist,:)' - S_pure(ist,c));
153 end
154 w(ist,c,hkvec) = w(ist,c,hkvec) + Bcn * Pc{ist}(hnvec_c, hkvec_c);
155 end
156 nvec = pprod(nvec, kvec);
157 end
158 end
159 end
160 end
161 end
162 end
163 end
164 % Compute throughputs (per-station like Java)
165 for c=1:C
166 % denom = sum over i of (v(i,c) * w(i,c,hkvec))
167 denom = 0;
168 for ist=1:M
169 denom = denom + v(ist,c) * w(ist,c,hkvec);
170 end
171 % Per-station throughput: x(ist,c) = v(ist,c) * N(c) / denom
172 for ist=1:M
173 if denom > 0
174 x(ist,c,hkvec) = v(ist,c) * kvec(c) / denom;
175 else
176 x(ist,c,hkvec) = 0;
177 end
178 end
179 end
180 % Update queue lengths: L = x * w
181 for ist=1:M
182 for c=1:C
183 L{ist}(c,hkvec) = x(ist,c,hkvec) * w(ist,c,hkvec);
184 end
185 if size(S(ist,:)) == 1
186 ns = S(ist);
187 else
188 ns = S(ist,c);
189 end
190 switch sched(ist)
191 case SchedStrategy.PS
192 if ns > 1
193 for n=1:min(S(ist),sum(kvec))
194 for c=1:C
195 if kvec(c) > 0
196 hkvec_c = hashpop(oner(kvec,c),Nc,C,prods);
197 Pc{ist}(1 + n, hkvec) = Pc{ist}(1 + n, hkvec) + Dc(ist,c) * (1/n) * x(ist,c,hkvec) * Pc{ist}(1+(n-1), hkvec_c);
198 end
199 end
200 Pc{ist}(1 + 0, hkvec) = max(eps,1-sum(Pc{ist}(1 + (1:min(S(ist),sum(kvec))), hkvec)));
201 end
202 end
203 case SchedStrategy.FCFS
204 if all(D(ist,:)==D(ist,1))
205 if ns > 1
206 for n=1:(min(ns,sum(kvec))-1)
207 for c=1:C
208 if kvec(c) > 0
209 hkvec_c = hashpop(oner(kvec,c),Nc,C,prods);
210 Pc{ist}(1 + n, hkvec) = Pc{ist}(1 + n, hkvec) + Dc(ist,c) * (1/n) * x(ist,c,hkvec) * Pc{ist}(1+(n-1), hkvec_c);
211 end
212 end
213 Pc{ist}(1 + 0, hkvec) = max(eps,1-sum(Pc{ist}(1 + (1:min(ns,sum(kvec))), hkvec)));
214 end
215 end
216 else
217 nvec = pprod(kvec);
218 nvec = pprod(nvec, kvec); % Skip zero vector like Java
219 sumOfAllProbs = 0;
220 while nvec >= 0
221 % Use Nc and prods for hash (matches Java)
222 hnvec = hashpop(nvec,Nc,C,prods);
223 prob = 0;
224 for c=1:C
225 if nvec(c)>0
226 % Use Nc and prods for hash (matches Java)
227 hnvec_c = hashpop(oner(nvec,c),Nc,C,prods);
228 hkvec_c = hashpop(oner(kvec,c),Nc,C,prods);
229 % Bcn calculation using pure service times (matches Java getBcn)
230 Bcn = S_pure(ist,c);
231 if sum(nvec) > 1
232 sumVal = nvec*S_pure(ist,:)';
233 Bcn = Bcn + max(0,sum(nvec)-ns)/max(ns*(sum(nvec)-1), 1e-12) * (sumVal - S_pure(ist,c));
234 end
235 % Use 1/sum(nvec) instead of 1/nvec(c) to match Java
236 capacity_inv = 1/sum(nvec);
237 classProb = Bcn * capacity_inv * x(ist,c,hkvec) * Pc{ist}(hnvec_c, hkvec_c);
238 prob = prob + classProb;
239 end
240 end
241 Pc{ist}(hnvec, hkvec) = prob;
242 sumOfAllProbs = sumOfAllProbs + prob;
243 nvec = pprod(nvec, kvec);
244 end
245 Pc{ist}(1 + 0, hkvec) = max(1e-12, 1 - sumOfAllProbs);
246 end
247 end
248 end
249 kvec = pprod(kvec, Nc);
250end
251
252% Throughput - compute system throughput as N(c) / sum(w) like Java
253for c=1:C
254 totalResponseTime = 0;
255 for ist=1:M
256 totalResponseTime = totalResponseTime + w(ist,c,hkvec);
257 end
258 if totalResponseTime > 0
259 XN(c) = Nc(c) / totalResponseTime;
260 else
261 XN(c) = 0;
262 end
263end
264if M>1
265 XN = repmat(XN,M,1);
266end
267% Utilization Law: a c-server station holds D*X/c of its capacity. XN has been
268% replicated over the stations above, so read the per-class throughput from its
269% first row -- linear indexing XN(c) into the (M x R) replica would silently
270% return the WRONG class's throughput whenever M > 1.
271Xc = XN(1,:);
272for m = 1:M
273 for c = 1:C
274 if size(S(m,:)) == 1
275 ns = S(m);
276 else
277 ns = S(m,c);
278 end
279 UN(m,closedClasses(c)) = D(m,closedClasses(c)) * Xc(closedClasses(c)) / ns;
280 end
281end
282% Response time
283CN(1:M,closedClasses) = w(1:M,1:C,hkvec);
284for ist=1:M
285 QN(ist,closedClasses) = L{ist}(closedClasses,hkvec);
286end
287
288T = table(XN,CN,QN,UN); % for display purposes
289
290end