-
Notifications
You must be signed in to change notification settings - Fork 100
/
Copy pathstatsfunhelper.m
123 lines (117 loc) · 4.38 KB
/
statsfunhelper.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
function statsfun = statsfunhelper(inp1, inp2)
% 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.
%
% Special field name:
%
% If a function with fieldname 'norecord' is specified, then the
% corresponding function is called without outputs, and no field is created
% in the stats structure for that function.
%
% This is especially helpful to display text or graphical information only.
%
% Example:
%
% metrics.norecord = @(x) my_plotting_function_without_outputs(x);
% This file is part of Manopt: www.manopt.org.
% Original author: Nicolas Boumal, Dec. 17, 2014.
% Contributors:
% Change log:
% Jan 2, 2021 (NB):
% Passing S to thestatsfun explicitly for compatibility with Octave 6.
% Apr 4, 2025 (NB):
% Added special treatment for field name 'norecord', giving no output.
if (nargin == 1) && isstruct(inp1)
S = inp1;
elseif (nargin == 2)
S = struct(inp1, inp2);
else
error('statsfunhelper takes 1 or 2 inputs. If 1 input, it must be a structure.');
end
statsfun = @(problem, x, stats, store) thestatsfun(S, problem, x, stats, store);
function stats = thestatsfun(S, problem, x, stats, store)
names = fieldnames(S);
for it = 1 : length(names)
name = names{it};
fun = S.(name);
if ~strcmp('norecord', name)
switch nargin(fun)
case 1
stats.(name) = fun(x);
case 2
stats.(name) = fun(problem, x);
case 3
stats.(name) = fun(problem, x, stats);
case 4
stats.(name) = fun(problem, x, stats, store);
otherwise
error('The functions passed to statsfunhelper must take 1 to 4 inputs.');
end
else
switch nargin(fun)
case 1
fun(x);
case 2
fun(problem, x);
case 3
fun(problem, x, stats);
case 4
fun(problem, x, stats, store);
otherwise
error('The functions passed to statsfunhelper must take 1 to 4 inputs.');
end
end
end
end
end