LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
converged.m
1function bool = converged(self, it)
2% BOOL = CONVERGED(IT)
3%
4% Apply convergence test to the SolverLN iterations. As the solver keeps
5% iterating, this method maintains a moving avg of the recent results based
6% on which it averages across the layer the maximum queue-length error.
7% Convergence is tested by resetting all layers (to avoid caching) and
8% doing an extra iteration. If the iteration keeps fulfilling the error
9% requirements for convergence, then the solver completes.
10
11bool = false;
12
13%% Stochastic iteration dispatch
14% When one or more layer solvers return noisy estimates (simulation or
15% Monte Carlo integration), the successive-difference test below cannot
16% terminate and the Robbins-Monro controller in convergedStoch.m is used
17% instead. In 'crn' mode the layer seeds are pinned in pre(), so the
18% layer maps are deterministic given the seeds and the standard test
19% below remains applicable.
20if ~isempty(self.stochiter_mode)
21 if self.stochiter_auto && strcmp(self.stochiter_mode,'off') && it >= 1 && any(self.stochlayers)
22 % a layer with method 'default' resolved at runtime to a
23 % stochastic method (captured in analyze() at iteration 1)
24 self.stochiter_mode = 'rm';
25 line_debug('LN: stochastic layer method detected at runtime, switching to Robbins-Monro iteration');
26 end
27 if strcmp(self.stochiter_mode,'rm')
28 bool = convergedStoch(self, it);
29 return
30 end
31end
32
33iter_min = max([2*length(self.model.ensemble),ceil(self.options.iter_max/4)]);
34E = self.nlayers;
35results = self.results; %#ok<NASGU> % faster in matlab
36
37%% Start moving average to help convergence
38
39if false%it>self.averagingstart%<self.averagingstart+50
40 % In the first 50 averaging iterations use Cesaro summation
41 if ~isempty(self.averagingstart)
42 if it>=iter_min % assume steady-state
43 for e=1:E
44 wnd_size_max = (it-self.averagingstart+1);
45 sk_q = cell(1,wnd_size_max);
46 sk_u = cell(1,wnd_size_max);
47 sk_r = cell(1,wnd_size_max);
48 sk_t = cell(1,wnd_size_max);
49 sk_a = cell(1,wnd_size_max);
50 sk_w = cell(1,wnd_size_max);
51 % compute all partial sumbs of up to wnd_size_max elements
52 for k= 1:wnd_size_max
53 if k==1
54 sk_q{k} = results{self.averagingstart,e}.QN;
55 sk_u{k} = results{self.averagingstart,e}.UN;
56 sk_r{k} = results{self.averagingstart,e}.RN;
57 sk_t{k} = results{self.averagingstart,e}.TN;
58 sk_a{k} = results{self.averagingstart,e}.AN;
59 sk_w{k} = results{self.averagingstart,e}.WN;
60 else
61 sk_q{k} = results{self.averagingstart+k-1,e}.QN/k + sk_q{k-1}*(k-1)/k;
62 sk_u{k} = results{self.averagingstart+k-1,e}.UN/k + sk_u{k-1}*(k-1)/k;
63 sk_r{k} = results{self.averagingstart+k-1,e}.RN/k + sk_r{k-1}*(k-1)/k;
64 sk_t{k} = results{self.averagingstart+k-1,e}.TN/k + sk_t{k-1}*(k-1)/k;
65 sk_a{k} = results{self.averagingstart+k-1,e}.AN/k + sk_a{k-1}*(k-1)/k;
66 sk_w{k} = results{self.averagingstart+k-1,e}.WN/k + sk_w{k-1}*(k-1)/k;
67 end
68 end
69 results{end,e}.QN = cellsum(sk_q)/wnd_size_max;
70 results{end,e}.UN = cellsum(sk_u)/wnd_size_max;
71 results{end,e}.RN = cellsum(sk_r)/wnd_size_max;
72 results{end,e}.TN = cellsum(sk_t)/wnd_size_max;
73 results{end,e}.AN = cellsum(sk_a)/wnd_size_max;
74 results{end,e}.WN = cellsum(sk_w)/wnd_size_max;
75 end
76 end
77 end
78else
79 wnd_size = max(5,ceil(iter_min/5)); % moving window size
80 mov_avg_weight = 1/wnd_size;
81 results = self.results; % faster in matlab
82 if it>=iter_min % assume steady-state
83 for e=1:E
84 results{end,e}.QN = mov_avg_weight*results{end,e}.QN;
85 results{end,e}.UN = mov_avg_weight*results{end,e}.UN;
86 results{end,e}.RN = mov_avg_weight*results{end,e}.RN;
87 results{end,e}.TN = mov_avg_weight*results{end,e}.TN;
88 results{end,e}.AN = mov_avg_weight*results{end,e}.AN;
89 results{end,e}.WN = mov_avg_weight*results{end,e}.WN;
90 for k=1:(wnd_size-1)
91 results{end,e}.QN = results{end,e}.QN + results{end-k,e}.QN * mov_avg_weight;
92 results{end,e}.UN = results{end,e}.UN + results{end-k,e}.UN * mov_avg_weight;
93 results{end,e}.RN = results{end,e}.RN + results{end-k,e}.RN * mov_avg_weight;
94 results{end,e}.TN = results{end,e}.TN + results{end-k,e}.TN * mov_avg_weight;
95 results{end,e}.AN = results{end,e}.AN + results{end-k,e}.AN * mov_avg_weight;
96 results{end,e}.WN = results{end,e}.WN + results{end-k,e}.WN * mov_avg_weight;
97 end
98 end
99 end
100end
101self.results = results;
102
103%% Take as error metric the max qlen-error averaged across layers
104if it>1
105 self.maxitererr(it) = 0;
106 for e = 1:E
107 metric = results{end,e}.QN;
108 metric_1 = results{end-1,e}.QN;
109 N = sum(self.ensemble{e}.getNumberOfJobs);
110 if N>0
111 try
112 diffvals = abs(metric(:) - metric_1(:));
113 diffvals(isnan(diffvals)) = 0;
114 IterErr = max(diffvals)/N;
115 catch
116 IterErr = 0;
117 end
118 self.maxitererr(it) = self.maxitererr(it) + IterErr;
119 end
120 % if self.options.verbose
121 % if self.solvers{e}.options.verbose
122 % line_printf(sprintf('QLen change: %f.\n',self.maxitererr(it)/E));
123 % elseif e==1
124 % line_printf('\n');
125 % end
126 % end
127 end
128 if it==iter_min
129 if self.options.verbose
130 line_printf( '\b Started averaging to aid convergence.');
131 end
132 self.averagingstart = it;
133 end
134
135 % Print iteration error for tracing
136 if self.options.verbose
137 line_printf(sprintf('MaxIterErr=%.6e (tol=%.6e, hasconv=%d)', ...
138 self.maxitererr(it), self.options.iter_tol, self.hasconverged));
139 end
140
141 %% Update relaxation factor for adaptive/auto modes
142 relax_mode = self.options.config.relax;
143 if strcmpi(relax_mode, 'adaptive') || strcmpi(relax_mode, 'auto')
144 % Track error history
145 self.relax_err_history = [self.relax_err_history, self.maxitererr(it)];
146 wnd = self.options.config.relax_history;
147 if length(self.relax_err_history) > wnd
148 self.relax_err_history = self.relax_err_history(end-wnd+1:end);
149 end
150
151 if length(self.relax_err_history) >= 3
152 % Detect oscillation by counting sign changes in error differences
153 err = self.relax_err_history;
154 diff_err = diff(err);
155 sign_changes = sum(diff_err(1:end-1) .* diff_err(2:end) < 0);
156
157 if strcmpi(relax_mode, 'auto') && self.relax_omega == 1.0
158 % For 'auto' mode: enable relaxation when oscillation detected
159 if sign_changes >= length(diff_err) * 0.5
160 self.relax_omega = self.options.config.relax_factor;
161 % Debug output removed
162 if self.options.verbose
163 % line_printf(sprintf(' [enabling relaxation, omega=%.2f]', self.relax_omega));
164 end
165 end
166 elseif strcmpi(relax_mode, 'adaptive')
167 % For 'adaptive' mode: adjust omega based on error trajectory
168 if sign_changes >= length(diff_err) * 0.5
169 % Oscillating - reduce omega
170 self.relax_omega = max(self.options.config.relax_min, self.relax_omega * 0.8);
171 % Debug output removed
172 if self.options.verbose
173 % line_printf(sprintf(' [omega=%.2f]', self.relax_omega));
174 end
175 elseif sign_changes == 0 && self.maxitererr(it) < self.maxitererr(it-1)
176 % Monotonically decreasing - can increase omega slightly
177 self.relax_omega = min(1.0, self.relax_omega * 1.05);
178 end
179 end
180 end
181 end
182end
183
184%% Check convergence. Do not allow to converge in less than 2 iterations.
185if it==0 && self.options.verbose
186 % Debug output removed
187elseif it>iter_min && self.maxitererr(it) < self.options.iter_tol && self.maxitererr(it-1) < self.options.iter_tol && self.maxitererr(it-2) < self.options.iter_tol
188 if ~self.hasconverged % if potential convergence has just been detected
189 % do a hard reset of every layer to check that this is really the fixed point
190 for e=1:E
191 self.ensemble{e}.reset();
192 end
193 if self.options.verbose
194 % Debug output removed
195 end
196 self.hasconverged = true; % if it passes the change again next time then complete
197 else
198 if self.options.verbose
199 if self.solvers{end}.options.verbose
200 % Debug output removed
201 end
202 end
203 bool = true;
204 end
205else
206 self.hasconverged = false;
207end
208end
Definition Station.m:245