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