LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
StationaryDistribution.m
1classdef StationaryDistribution < handle
2 properties (Access = private)
3 process
4 is_binded = false
5 rho
6 is_rho_computed = false
7 R = []
8 G = []
9 pi_0_c = {}
10 mean_cl = []
11 is_mean_clients_computed = false
12 sum_from_c_to_inf = []
13 end
14
15 methods
16 function obj = StationaryDistribution(proc)
17 if nargin > 0
18 obj.bind(proc);
19 end
20 end
21
22 function bind(obj, proc)
23 obj.process = proc;
24 obj.is_binded = true;
25 obj.is_rho_computed = false;
26 obj.is_mean_clients_computed = false;
27 obj.R = [];
28 obj.G = [];
29 obj.pi_0_c = {};
30 obj.mean_cl = [];
31 obj.sum_from_c_to_inf = [];
32 end
33
34 function value = get_rho(obj)
35 obj.compute_rho();
36 value = obj.rho;
37 end
38
39 function matrix = get_R(obj)
40 obj.compute_R();
41 matrix = obj.R;
42 end
43
44 function matrix = get_G(obj)
45 obj.compute_G();
46 matrix = obj.G;
47 end
48
49 function dist = get_dist(obj, max_level)
50 obj.compute_pi_0_c();
51 dist = obj.pi_0_c(1:min(max_level + 1, numel(obj.pi_0_c)));
52 pi = obj.pi_0_c{end};
53 for k = numel(dist):max_level
54 pi = pi * obj.R;
55 dist{end + 1} = pi;
56 end
57 end
58
59 function dist = get_pi_0_c(obj)
60 obj.compute_pi_0_c();
61 dist = obj.pi_0_c;
62 end
63
64 function value = get_mean_clients(obj)
65 if obj.is_mean_clients_computed
66 value = obj.mean_cl;
67 return;
68 end
69
70 obj.compute_rho();
71 if obj.rho >= 1
72 value = inf;
73 return;
74 end
75
76 obj.compute_pi_0_c();
77 value = 0.0;
78 for k = 2:(numel(obj.pi_0_c) - 1)
79 value = value + (k - 1) * sum(obj.pi_0_c{k});
80 end
81
82 obj.compute_R();
83 I = eye(size(obj.R));
84 tmp = (I - obj.R) \ I;
85 pi = obj.pi_0_c{end};
86 value = value + sum(pi * ((obj.R * tmp) + (numel(obj.pi_0_c) - 1) * I) * tmp);
87 obj.mean_cl = value;
88 obj.is_mean_clients_computed = true;
89 end
90
91 function value = get_sum_from_c_to_inf(obj)
92 if isempty(obj.sum_from_c_to_inf)
93 obj.compute_pi_0_c();
94 I = eye(size(obj.R));
95 obj.sum_from_c_to_inf = ((I - obj.R).') \ obj.pi_0_c{end}.';
96 obj.sum_from_c_to_inf = obj.sum_from_c_to_inf.';
97 end
98 value = obj.sum_from_c_to_inf;
99 end
100
101 function value = get_mean_queue(obj, queue_size_vector)
102 obj.compute_pi_0_c();
103 if numel(queue_size_vector) ~= numel(obj.pi_0_c)
104 libqbd.raise_error('You need to specify c first levels.');
105 end
106
107 obj.compute_rho();
108 if obj.rho >= 1
109 value = inf;
110 return;
111 end
112
113 value = 0.0;
114 for k = 1:(numel(obj.pi_0_c) - 1)
115 value = value + sum(libqbd.as_row_vector(obj.pi_0_c{k}) .* libqbd.as_row_vector(queue_size_vector{k}));
116 end
117
118 I = eye(size(obj.R));
119 tmp = (I - obj.R).';
120 pi = obj.pi_0_c{end}.';
121 queue_tail = libqbd.as_row_vector(queue_size_vector{end});
122 value = value + sum(((tmp \ pi).') .* queue_tail);
123 value = value + sum((tmp \ (tmp \ ((obj.pi_0_c{end} * obj.R).'))));
124 end
125 end
126
127 methods (Access = private)
128 function check(obj)
129 if ~obj.is_binded
130 libqbd.raise_error('Not binded to the process.');
131 end
132 if isempty(obj.process.all_A_0())
133 libqbd.raise_error('Infinitesimal generator matrix is empty.');
134 end
135 end
136
137 function compute_rho(obj)
138 if obj.is_rho_computed
139 return;
140 end
141
142 obj.check();
143 am = obj.process.all_A_minus();
144 a0 = obj.process.all_A_0();
145 ap = obj.process.all_A_plus();
146 A = am{end} + a0{end} + ap{end};
147 A(:, 1) = 1.0;
148 r = zeros(size(A, 1), 1);
149 r(1) = 1.0;
150 alpha = (A.' \ r).';
151 obj.rho = sum(alpha * ap{end}) / sum(alpha * am{end});
152 obj.is_rho_computed = true;
153 end
154
155 function compute_G(obj)
156 if ~isempty(obj.G)
157 return;
158 end
159
160 obj.check();
161 obj.compute_rho();
162 if obj.rho >= 1
163 libqbd.raise_error('rho is equal or greater than 1.');
164 end
165
166 am = obj.process.all_A_minus();
167 a0 = obj.process.all_A_0();
168 ap = obj.process.all_A_plus();
169 A_m = am{end};
170 A_0 = a0{end};
171 A_p = ap{end};
172 T = -(A_0 \ eye(size(A_0)));
173 V_m = T * A_m;
174 V_p = T * A_p;
175 I = eye(size(T));
176 W = (I - V_m * V_p - V_p * V_m) \ I;
177 U = I;
178 G = V_m;
179 tol = realmin(class(G));
180 while true
181 U = U * V_p;
182 V_m = W * V_m * V_m;
183 V_p = W * V_p * V_p;
184 W = (I - V_m * V_p - V_p * V_m) \ I;
185 T_prev = G;
186 G = G + U * V_m;
187 delta = T_prev - G;
188 if max(abs(delta(:))) <= tol
189 break;
190 end
191 end
192 obj.G = G;
193 end
194
195 function compute_R(obj)
196 if ~isempty(obj.R)
197 return;
198 end
199
200 obj.check();
201 obj.compute_G();
202 a0 = obj.process.all_A_0();
203 ap = obj.process.all_A_plus();
204 A_0 = a0{end};
205 A_p = ap{end};
206 U = -(A_0 + A_p * obj.G);
207 obj.R = A_p / U;
208 end
209
210 function compute_pi_0_c(obj)
211 obj.check();
212 if ~isempty(obj.pi_0_c)
213 return;
214 end
215
216 obj.compute_rho();
217 if obj.rho >= 1
218 libqbd.raise_error('rho is equal or greater than 1.');
219 end
220
221 c = numel(obj.process.all_A_0());
222 if c == 0
223 libqbd.raise_error('Generator matrix is empty.');
224 elseif c > 1
225 c = c - 1;
226 end
227 c = max(c, 1);
228
229 matrix_len = 0;
230 for k = 0:c
231 matrix_len = matrix_len + size(obj.process.get_A_0(k), 1);
232 end
233
234 B = zeros(matrix_len, matrix_len);
235 A00 = obj.process.get_A_0(0);
236 Ap0 = obj.process.get_A_plus(0);
237 B(1:size(A00, 1), 1:size(A00, 2)) = A00;
238 B(1:size(Ap0, 1), (size(A00, 2) + 1):(size(A00, 2) + size(Ap0, 2))) = Ap0;
239
240 row_offset = size(A00, 1) + 1;
241 col_offset = 0;
242 for k = 1:(c - 1)
243 A_minus = obj.process.get_A_minus(k);
244 A_0 = obj.process.get_A_0(k);
245 A_plus = obj.process.get_A_plus(k);
246 B(row_offset:(row_offset + size(A_minus, 1) - 1), (col_offset + 1):(col_offset + size(A_minus, 2))) = A_minus;
247 col_offset = col_offset + size(A_minus, 2);
248 B(row_offset:(row_offset + size(A_0, 1) - 1), (col_offset + 1):(col_offset + size(A_0, 2))) = A_0;
249 B(row_offset:(row_offset + size(A_plus, 1) - 1), (col_offset + size(A_0, 2) + 1):(col_offset + size(A_0, 2) + size(A_plus, 2))) = A_plus;
250 row_offset = row_offset + size(A_0, 1);
251 end
252
253 A_minus = obj.process.get_A_minus(c);
254 B(row_offset:(row_offset + size(A_minus, 1) - 1), (col_offset + 1):(col_offset + size(A_minus, 2))) = A_minus;
255 col_offset = col_offset + size(A_minus, 2);
256 obj.compute_R();
257 A_tail = obj.process.get_A_0(c) + obj.R * obj.process.get_A_minus(c + 1);
258 B(row_offset:(row_offset + size(A_tail, 1) - 1), (col_offset + 1):(col_offset + size(A_tail, 2))) = A_tail;
259
260 norm_eq = ones(size(B, 1), 1);
261 I = eye(size(obj.R));
262 ones_tail = ones(size(obj.R, 1), 1);
263 norm_eq(end - size(obj.R, 1) + 1:end) = (I - obj.R) \ ones_tail;
264 B(:, 1) = norm_eq;
265 right = zeros(size(B, 1), 1);
266 right(1) = 1.0;
267 dist = (B.' \ right).';
268 dist(dist < 0.0) = 0.0;
269
270 left = 1;
271 for k = 0:c
272 right_idx = left + size(obj.process.get_A_0(k), 1) - 1;
273 obj.pi_0_c{end + 1} = dist(left:right_idx);
274 left = right_idx + 1;
275 end
276 end
277 end
278end