Helper tool to create a statsfun for the options structure of solvers. function statsfun = statsfunhelper(name, fun) function statsfun = statsfunhelper(S) Usage with (name, fun): Input 1: name is a string which is a valid field name (no spaces, starts with a letter or an underscore, only alphanumeric characters and underscores). Input2: fun is a function handle with one output and 1 to 4 inputs, as follows (your choice): fun(x) or fun(problem, x) or fun(problem, x, stats) or fun(problem, x, stats, store) where the inputs are the ones that would be given to options.statsfun, as described in the help of the solver used. Typically, x is the point on the manifold at the current iterate, problem is the Manopt problem structure, stats is all the current statistics recorded for that iterate and store is the cache structure at the current iterate. When calling a Manopt solver with the options structure, such as for example with: [x, xcost, info] = steepestdescent(problem, [], options); you may set a field of the options structure as follows: options.statsfun = statsfunhelper('nameofthefield', fun); As a result, at each iteration, the stats structure will contain a field stats.nameofthefield with the value returned by the call to fun at that iterate. The stats structures are stored in the struct-array info. As an example, if the value returned by fun is a scalar, then [info.nameofthefield] is a vector containing all returned values. Usage with S: The input S is a structure. For each field of S, say S.field, the stats structure will be augmented with stats.field = fun(..), where fun is the function handle stored in S.field, and with the same conventions as above. This version allows to record more than one bit of information at each iteration. Example: metrics.nameofthefield = fun; metrics.othername = otherfun; options.statsfun = statsfunhelper(metrics); The different function handles (here, fun and otherfun) can take 1 to 4 inputs too, and they do not have to take the same number of inputs.
0001 function statsfun = statsfunhelper(inp1, inp2) 0002 % Helper tool to create a statsfun for the options structure of solvers. 0003 % 0004 % function statsfun = statsfunhelper(name, fun) 0005 % function statsfun = statsfunhelper(S) 0006 % 0007 % Usage with (name, fun): 0008 % 0009 % Input 1: name is a string which is a valid field name (no spaces, starts 0010 % with a letter or an underscore, only alphanumeric characters and 0011 % underscores). 0012 % 0013 % Input2: fun is a function handle with one output and 1 to 4 inputs, as 0014 % follows (your choice): 0015 % 0016 % fun(x) or fun(problem, x) or 0017 % fun(problem, x, stats) or fun(problem, x, stats, store) 0018 % 0019 % where the inputs are the ones that would be given to options.statsfun, as 0020 % described in the help of the solver used. Typically, x is the point on 0021 % the manifold at the current iterate, problem is the Manopt problem 0022 % structure, stats is all the current statistics recorded for that iterate 0023 % and store is the cache structure at the current iterate. 0024 % 0025 % When calling a Manopt solver with the options structure, such as for 0026 % example with: 0027 % 0028 % [x, xcost, info] = steepestdescent(problem, [], options); 0029 % 0030 % you may set a field of the options structure as follows: 0031 % 0032 % options.statsfun = statsfunhelper('nameofthefield', fun); 0033 % 0034 % As a result, at each iteration, the stats structure will contain a field 0035 % stats.nameofthefield with the value returned by the call to fun at that 0036 % iterate. The stats structures are stored in the struct-array info. 0037 % As an example, if the value returned by fun is a scalar, then 0038 % [info.nameofthefield] is a vector containing all returned values. 0039 % 0040 % 0041 % Usage with S: 0042 % 0043 % The input S is a structure. For each field of S, say S.field, the stats 0044 % structure will be augmented with stats.field = fun(..), where fun is the 0045 % function handle stored in S.field, and with the same conventions as 0046 % above. This version allows to record more than one bit of information at 0047 % each iteration. Example: 0048 % 0049 % metrics.nameofthefield = fun; 0050 % metrics.othername = otherfun; 0051 % options.statsfun = statsfunhelper(metrics); 0052 % 0053 % The different function handles (here, fun and otherfun) can take 1 to 4 0054 % inputs too, and they do not have to take the same number of inputs. 0055 0056 % This file is part of Manopt: www.manopt.org. 0057 % Original author: Nicolas Boumal, Dec. 17, 2014. 0058 % Contributors: 0059 % Change log: 0060 % Jan 2, 2021 (NB): 0061 % Passing S to thestatsfun explicitly for compatibility with Octave 6. 0062 0063 if (nargin == 1) && isstruct(inp1) 0064 S = inp1; 0065 elseif (nargin == 2) 0066 S = struct(inp1, inp2); 0067 else 0068 error('statsfunhelper takes 1 or 2 inputs. If 1 input, it must be a structure.'); 0069 end 0070 0071 statsfun = @(problem, x, stats, store) thestatsfun(S, problem, x, stats, store); 0072 0073 0074 function stats = thestatsfun(S, problem, x, stats, store) 0075 names = fieldnames(S); 0076 for it = 1 : length(names) 0077 name = names{it}; 0078 fun = S.(name); 0079 switch nargin(fun) 0080 case 1 0081 stats.(name) = fun(x); 0082 case 2 0083 stats.(name) = fun(problem, x); 0084 case 3 0085 stats.(name) = fun(problem, x, stats); 0086 case 4 0087 stats.(name) = fun(problem, x, stats, store); 0088 otherwise 0089 error('The functions passed to statsfunhelper must take 1 to 4 inputs.'); 0090 end 0091 end 0092 end 0093 0094 end