1function
bool = converged(self, it)
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.
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');
27 if strcmp(self.stochiter_mode,'rm')
28 bool = convergedStoch(self, it);
33iter_min = max([2*length(self.model.ensemble),ceil(self.options.iter_max/4)]);
35results = self.results; %
#ok<NASGU> % faster in matlab
37%% Start moving average to help convergence
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
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
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;
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;
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;
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
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;
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;
101self.results = results;
103%% Take as error metric
the max qlen-error averaged across layers
105 self.maxitererr(it) = 0;
107 metric = results{end,e}.QN;
108 metric_1 = results{end-1,e}.QN;
109 N = sum(self.ensemble{e}.getNumberOfJobs);
112 diffvals = abs(metric(:) - metric_1(:));
113 diffvals(isnan(diffvals)) = 0;
114 IterErr = max(diffvals)/N;
118 self.maxitererr(it) = self.maxitererr(it) + IterErr;
120 %
if self.options.verbose
121 %
if self.solvers{e}.options.verbose
122 % line_printf(sprintf(
'QLen change: %f.\n',self.maxitererr(it)/E));
129 if self.options.verbose
130 line_printf(
'\b Started averaging to aid convergence.');
132 self.averagingstart = it;
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));
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);
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);
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));
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));
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);
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
191 self.ensemble{e}.reset();
193 if self.options.verbose
194 % Debug output removed
196 self.hasconverged =
true; %
if it passes
the change again next time then complete
198 if self.options.verbose
199 if self.solvers{end}.options.verbose
200 % Debug output removed
206 self.hasconverged =
false;