Checks for standard stopping criteria, as a helper to solvers. function [stop, reason] = stoppingcriterion(problem, x, options, info, last) Executes standard stopping criterion checks, based on what is defined in the info(last) stats structure and in the options structure. The returned number 'stop' is 0 if none of the stopping criteria triggered, and a (strictly) positive integer otherwise. The integer identifies which criterion triggered: 0 : Nothing triggered; 1 : Cost tolerance reached; 2 : Gradient norm tolerance reached; 3 : Max time exceeded; 4 : Max iteration count reached; 6 : User defined stopfun criterion triggered. The output 'reason' is a string describing the triggered event.
0001 function [stop, reason] = stoppingcriterion(problem, x, options, info, last) 0002 % Checks for standard stopping criteria, as a helper to solvers. 0003 % 0004 % function [stop, reason] = stoppingcriterion(problem, x, options, info, last) 0005 % 0006 % Executes standard stopping criterion checks, based on what is defined in 0007 % the info(last) stats structure and in the options structure. 0008 % 0009 % The returned number 'stop' is 0 if none of the stopping criteria 0010 % triggered, and a (strictly) positive integer otherwise. The integer 0011 % identifies which criterion triggered: 0012 % 0 : Nothing triggered; 0013 % 1 : Cost tolerance reached; 0014 % 2 : Gradient norm tolerance reached; 0015 % 3 : Max time exceeded; 0016 % 4 : Max iteration count reached; 0017 % 6 : User defined stopfun criterion triggered. 0018 % 0019 % The output 'reason' is a string describing the triggered event. 0020 0021 % This file is part of Manopt: www.manopt.org. 0022 % Original author: Nicolas Boumal, Dec. 30, 2012. 0023 % Contributors: 0024 % Change log: 0025 % 0026 % Apr. 2, 2015 (NB): 0027 % 'reason' now contains the option (name and value) that triggered. 0028 % 0029 % Aug. 3, 2018 (NB): 0030 % Removed check for costevals, as it was never used, and the new 0031 % manopt counters allow to do this in a more transparent way. 0032 % Furthermore, now, options.stopfun can have 1 or 2 outputs: the 0033 % first is a boolean indicating whether or not to stop, and the 0034 % (optional) second output is a string indicating the reason. 0035 0036 0037 stop = 0; 0038 reason = ''; 0039 0040 stats = info(last); 0041 0042 % Target cost attained 0043 if isfield(stats, 'cost') && isfield(options, 'tolcost') && ... 0044 stats.cost <= options.tolcost 0045 reason = sprintf('Cost tolerance reached; options.tolcost = %g.', options.tolcost); 0046 stop = 1; 0047 return; 0048 end 0049 0050 % Target gradient norm attained 0051 if isfield(stats, 'gradnorm') && isfield(options, 'tolgradnorm') && ... 0052 stats.gradnorm < options.tolgradnorm 0053 reason = sprintf('Gradient norm tolerance reached; options.tolgradnorm = %g.', options.tolgradnorm); 0054 stop = 2; 0055 return; 0056 end 0057 0058 % Allotted time exceeded 0059 if isfield(stats, 'time') && isfield(options, 'maxtime') && ... 0060 stats.time >= options.maxtime 0061 reason = sprintf('Max time exceeded; options.maxtime = %g.', options.maxtime); 0062 stop = 3; 0063 return; 0064 end 0065 0066 % Allotted iteration count exceeded 0067 if isfield(stats, 'iter') && isfield(options, 'maxiter') && ... 0068 stats.iter >= options.maxiter 0069 reason = sprintf('Max iteration count reached; options.maxiter = %g.', options.maxiter); 0070 stop = 4; 0071 return; 0072 end 0073 0074 % Check whether the possibly user defined stopping criterion 0075 % triggers or not. 0076 if isfield(options, 'stopfun') 0077 % options.stopfun can have 1 or 2 outputs, but checking this with 0078 % nargout does not always work because it is technical to determine 0079 % for anonymous functions. Thus, we use our best guess. Nargout 0080 % returns -1 when it cannot determine the number of outputs, in 0081 % which case we take the safer approach of assuming 1 output. 0082 switch nargout(options.stopfun) 0083 case 2 0084 [userstop, reason] = options.stopfun(problem, x, info, last); 0085 case {1, -1} 0086 userstop = options.stopfun(problem, x, info, last); 0087 reason = ['User defined stopfun criterion triggered; ' ... 0088 'see options.stopfun.']; 0089 otherwise 0090 error('manopt:stoppingcriterion:stopfunoutputs', ... 0091 'options.stopfun must have one or two outputs.'); 0092 end 0093 if userstop 0094 stop = 6; 0095 if nargout(options.stopfun) == -1 0096 reason = [reason, '\n(A reason may have been ' ... 0097 'provided, but stoppingcriterion was ' ... 0098 'unable to determine\nthe number of ' ... 0099 'output arguments of options.stopfun.)']; 0100 end 0101 return; 0102 end 0103 end 0104 0105 end