comparison scripts/ode/odeset.m @ 20620:e5f36a7854a5

Remove fuzzy matching from odeset/odeget. * levenshtein.cc: Deleted file. * libinterp/corefcn/module.mk: Remove levenshtein.cc from build system. * fuzzy_compare.m: Deleted file. * scripts/ode/module.mk: Remove fuzzy_compare.m from build system * odeget.m: Reword docstring. Use a persistent cellstr variable to keep track of all options. Replace fuzzy_compare() calls with combination of strcmpi and strncmpi. Report errors relative to function odeget rather than OdePkg. Rewrite and extend BIST tests. Add input validation BIST tests. * odeset.m: Reword docstring. Use a persistent cellstr variable to keep track of all options. Replace fuzzy_compare() calls with combination of strcmpi and strncmpi. Report errors relative to function odeset rather than OdePkg. Use more meaningful variables names and create intermediate variables with logical names to help make code readable. Remove interactive input when multiple property names match and just issue an error. Rewrite BIST tests. * ode_struct_value_check.m: Remove input checking for private function which must always be invoked correctly by caller. Use intermediate variables opt and val to make the code more understandable. Consolidate checks on values into single if statements. Use 'val == fix (val)' to check for integer. * __unimplemented__.m: Removed odeset, odeget, ode45 from list.
author Rik <rik@octave.org>
date Fri, 09 Oct 2015 12:03:23 -0700
parents 45151de7423f
children
comparison
equal deleted inserted replaced
20619:eef93a493ce3 20620:e5f36a7854a5
23 ## @deftypefnx {Function File} {@var{odestruct} =} odeset (@var{oldstruct}, @var{"field1"}, @var{value1}, @var{"field2"}, @var{value2}, @dots{}) 23 ## @deftypefnx {Function File} {@var{odestruct} =} odeset (@var{oldstruct}, @var{"field1"}, @var{value1}, @var{"field2"}, @var{value2}, @dots{})
24 ## @deftypefnx {Function File} {@var{odestruct} =} odeset (@var{oldstruct}, @var{newstruct}) 24 ## @deftypefnx {Function File} {@var{odestruct} =} odeset (@var{oldstruct}, @var{newstruct})
25 ## 25 ##
26 ## Create or modify an ODE options structure. 26 ## Create or modify an ODE options structure.
27 ## 27 ##
28 ## If this function is called without an input argument then return a new ODE 28 ## When called without an input argument, return a new ODE options structure
29 ## options structure array that contains all the necessary fields and sets 29 ## that contains all possible fields initialized to their default values.
30 ## the values of all fields to default values. 30 ##
31 ## 31 ## If called with string input arguments @var{"field1"}, @var{"field2"},
32 ## If this function is called with string input arguments @var{"field1"}, 32 ## @dots{} identifying valid ODE options then return a new ODE options
33 ## @var{"field2"}, @dots{} identifying valid ODE options then return a new 33 ## structure with all possible fields initialized @strong{and} set the values
34 ## ODE options structure with all necessary fields and set the values of the 34 ## of the fields @var{"field1"}, @var{"field2"}, @dots{} to the values
35 ## fields @var{"field1"}, @var{"field2"}, @dots{} to the values @var{value1}, 35 ## @var{value1}, @var{value2}, @dots{}
36 ## @var{value2}, @dots{} 36 ##
37 ## 37 ## If called with an input structure @var{oldstruct} then overwrite the values
38 ## If this function is called with a first input argument @var{oldstruct} of 38 ## of the options @var{"field1"}, @var{"field2"}, @dots{} with new values
39 ## type structure array then overwrite all values of the options 39 ## @var{value1}, @var{value2}, @dots{} and return the modified structure.
40 ## @var{"field1"}, @var{"field2"}, @dots{} of the structure @var{oldstruct} 40 ##
41 ## with new values @var{value1}, @var{value2}, @dots{} and return the 41 ## When called with two input ODE options structures @var{oldstruct} and
42 ## modified structure array. 42 ## @var{newstruct} overwrite all values from the structure @var{oldstruct} with
43 ## 43 ## new values from the structure @var{newstruct}. Empty values in
44 ## If this function is called with two input arguments @var{oldstruct} and 44 ## @var{newstruct} will not overwrite values in @var{oldstruct}.
45 ## @var{newstruct} of type structure array then overwrite all values in the
46 ## fields from the structure @var{oldstruct} with new values of the fields
47 ## from the structure @var{newstruct}. Empty values of @var{newstruct} will
48 ## not overwrite values in @var{oldstruct}.
49 ## @seealso{odeget} 45 ## @seealso{odeget}
50 ## @end deftypefn 46 ## @end deftypefn
51 47
52 function odestruct = odeset (varargin) 48 function odestruct = odeset (varargin)
53 49
55 if (nargin == 0 && nargout == 0) 51 if (nargin == 0 && nargout == 0)
56 print_options (); 52 print_options ();
57 return; 53 return;
58 endif 54 endif
59 55
60 ## Column vector of all possible OdePkg fields 56 ## Column vector of all possible OdePkg options
61 fields = ["AbsTol"; "Algorithm"; "BDF"; "Choice"; "Eta"; "Events"; 57 persistent options = {"AbsTol"; "Algorithm"; "BDF"; "Choice"; "Eta"; "Events";
62 "Explicit"; "InexactSolver"; "InitialSlope"; "InitialStep"; 58 "Explicit"; "InexactSolver"; "InitialSlope";
63 "Jacobian";"JConstant";"JPattern";"Mass"; "MassConstant"; 59 "InitialStep"; "Jacobian"; "JConstant"; "JPattern";
64 "MassSingular"; "MaxNewtonIterations"; "MaxOrder"; "MaxStep"; 60 "Mass"; "MassConstant"; "MassSingular";
65 "MStateDependence"; "MvPattern"; "NewtonTol"; "NonNegative"; 61 "MaxNewtonIterations"; "MaxOrder"; "MaxStep";
66 "NormControl"; "OutputFcn"; "OutputSave"; "OutputSel"; 62 "MStateDependence"; "MvPattern"; "NewtonTol";
67 "PolynomialDegree"; "QuadratureOrder"; "Refine"; "RelTol"; 63 "NonNegative"; "NormControl"; "OutputFcn"; "OutputSave";
68 "Restart"; "Stats"; "TimeStepNumber"; "TimeStepSize"; 64 "OutputSel"; "PolynomialDegree"; "QuadratureOrder";
69 "UseJacobian"; "Vectorized"]; 65 "Refine"; "RelTol"; "Restart"; "Stats";
70 66 "TimeStepNumber"; "TimeStepSize"; "UseJacobian";
71 fields_nb = rows (fields); 67 "Vectorized"};
72 68
73 ## initialize output 69 ## initialize output
74 odestruct = cell2struct (cell (rows (fields), 1), cellstr (fields)); 70 odestruct = cell2struct (cell (numel (options), 1), options);
75 71
76 odestruct.Refine = 0; 72 if (nargin == 0)
77 odestruct.OutputSave = 1;
78
79 if (nargin == 0 && nargout == 1)
80 return; 73 return;
81 endif 74 endif
82 75
83 ode_fields = fieldnames (odestruct);
84
85 if (isstruct (varargin{1})) 76 if (isstruct (varargin{1}))
86 oldstruct = varargin{1}; 77 oldstruct = varargin{1};
87 ode_struct_value_check (oldstruct); 78 ode_struct_value_check (oldstruct);
88 79
89 optA_fields = fieldnames (oldstruct); 80 oldstruct_fldnames = (fieldnames (oldstruct)).';
90 optA_f_nb = length (optA_fields); 81
91 82 ## Copy oldstruct values into output odestruct
92 ## loop on first struct options for updating 83 for fldname = oldstruct_fldnames
93 for i = 1:optA_f_nb 84 name = lower (fldname{1});
94 name = lower (deblank (optA_fields{i})); 85
95 86 exactmatch = true;
96 while (1) 87 match = find (strcmpi (name, options));
97 pos = fuzzy_compare (name, fields); 88 if (isempty (match))
98 if (isempty (pos)) 89 match = find (strncmpi (name, options, length (name)));
99 warning ("OdePkg:InvalidArgument", 90 exactmatch = false;
100 "no property found with name '%s'", name); 91 endif
101 endif 92
102 93 if (isempty (match))
103 if (rows (pos) == 1) 94 error ("odeset: invalid property '%s'", fldname{1});
104 if (! strcmp (lower (deblank (name)), 95 elseif (numel (match) == 1)
105 lower (deblank (fields(pos,:))))) 96 if (! exactmatch)
106 warning ("OdePkg:InvalidArgument", "no exact matching for ", 97 warning ("odeset:NoExactMatching",
107 "'%s'. Assuming you were intending '%s'", 98 "no exact match for '%s'. Assuming '%s'.",
108 name, deblank (fields(pos,:))); 99 name, options{match});
109 endif 100 endif
110 101 odestruct.(options{match}) = oldstruct.(fldname{1});
111 odestruct.(deblank (fields(pos,:))) = oldstruct.(optA_fields{i}); 102 else
112 break; 103 error ("odeset: no exact match for '%s'. Possible fields found: %s.",
113 endif 104 name, strjoin (options(match), ", "));
114 105 endif
115 ## FIXME: Do we really need interactive selection?
116 ## if there are more matching, ask the user to be more precise
117 warning ("OdePkg:InvalidArgument",
118 "no exact matching for '%s'. %d possible fields were found",
119 name, size(pos, 1));
120 for j = 1:(rows (pos))
121 printf ("%s\n", deblank (fields(pos(j),:)));
122 endfor
123 do
124 disp ("Please insert field name again");
125 name = input ("New field name: ");
126 until (ischar (name))
127 endwhile
128 endfor 106 endfor
107
108 ## At this point, odestruct has been initialized with default values,
109 ## and if oldstruct was present it has overwritten fields in odestruct.
129 110
130 if (nargin == 2 && isstruct (varargin{2})) 111 if (nargin == 2 && isstruct (varargin{2}))
131 newstruct = varargin{2}; 112 newstruct = varargin{2};
132 ode_struct_value_check (newstruct); 113 ode_struct_value_check (newstruct);
133 114
134 optB_fields = fieldnames (newstruct); 115 newstruct_fldnames = (fieldnames (newstruct)).';
135 optB_f_nb = length (optB_fields); 116
136 117 ## Update the first struct with the values from the second one
137 ## update the first struct with the values in the second one 118 for fldname = newstruct_fldnames
138 for i = 1:optB_f_nb 119 name = lower (fldname{1});
139 name = lower (deblank (optB_fields{i})); 120
140 while (1) 121 exactmatch = true;
141 pos = fuzzy_compare (name, fields); 122 match = find (strcmpi (name, options));
142 123 if (isempty (match))
143 if (isempty (pos)) 124 match = find (strncmpi (name, options, length (name)));
144 warning ("OdePkg:InvalidArgument", 125 exactmatch = false;
145 "no property found with name '%s'", name); 126 endif
127
128 if (isempty (match))
129 error ("odeset: invalid property '%s'", fldname{1});
130 elseif (numel (match) == 1)
131 if (! exactmatch)
132 warning ("odeset:NoExactMatching",
133 "no exact match for '%s'. Assuming '%s'.",
134 name, options{match});
146 endif 135 endif
147 136 odestruct.(options{match}) = newstruct.(fldname{1});
148 if (rows (pos) == 1) 137 else
149 if (! strcmp (lower (deblank (name)), 138 error ("odeset: no exact match for '%s'. Possible fields found: %s.",
150 lower (deblank (fields(pos,:))))) 139 name, strjoin (options(match), ", "));
151 warning ("OdePkg:InvalidArgument", "no exact matching for ", 140 endif
152 "'%s'. Assuming you were intending '%s'",
153 name, deblank (fields(pos,:)));
154 endif
155 odestruct.(deblank (fields(pos,:))) = newstruct.(optB_fields{i});
156 break;
157 endif
158
159 ## FIXME: Do we really need interactive selection?
160 ## if there are more matching, ask the user to be more precise
161 warning ("OdePkg:InvalidArgument", "no exact matching for '%s'. ",
162 "%d possible fields were found",
163 name, rows (pos));
164 for j = 1:(rows (pos))
165 printf ("%s\n", deblank (fields(pos(j),:)));
166 endfor
167 do
168 disp ("Please insert field name again");
169 name = input ("New field name: ");
170 until (ischar (name))
171 endwhile
172 endfor 141 endfor
142
143 ## Done copying newstruct to oldstruct
173 return; 144 return;
174 endif 145 endif
175 146
176 ## if the second argument is not a struct, 147 ## Second argument is not a struct
177 ## pass new values of the OdePkg options to the first struct
178 if (mod (nargin, 2) != 1) 148 if (mod (nargin, 2) != 1)
179 error ("odeset: FIELD/VALUE arguments must occur in pairs"); 149 error ("odeset: FIELD/VALUE arguments must occur in pairs");
180 endif 150 endif
181
182 if (! all (cellfun ("isclass", varargin(2:2:end), "char"))) 151 if (! all (cellfun ("isclass", varargin(2:2:end), "char")))
183 error ("odeset: All FIELD names must be strings"); 152 error ("odeset: All FIELD names must be strings");
184 endif 153 endif
185 154
186 ## loop on the input arguments 155 ## Write new field/value pairs into odestruct
187 for i = 2:2:(nargin - 1) 156 for i = 2:2:nargin
188 name = varargin{i}; 157 name = lower (varargin{i});
189 158
190 while (1) 159 exactmatch = true;
191 pos = fuzzy_compare (name, fields); 160 match = find (strcmpi (name, options));
192 161 if (isempty (match))
193 if (isempty (pos)) 162 match = find (strncmpi (name, options, length (name)));
194 error ("OdePkg:InvalidArgument", 163 exactmatch = false;
195 "no property found with name '%s'", name); 164 endif
196 endif 165
197 166 if (isempty (match))
198 if (rows (pos) == 1) 167 error ("odeset: invalid property '%s'", varargin{i});
199 if (! strcmp (lower (deblank (name)), 168 elseif (numel (match) == 1)
200 lower (deblank (fields(pos,:))))) 169 if (! exactmatch)
201 warning ("OdePkg:InvalidArgument", "no exact matching for '%s'. ", 170 warning ("odeset:NoExactMatching",
202 "%d possible fields were found", 171 "no exact match for '%s'. Assuming '%s'.",
203 name, rows (pos)); 172 name, options{match});
204 endif 173 endif
205 odestruct.(deblank (fields(pos,:))) = varargin{i+1}; 174 odestruct.(options{match}) = varargin{i+1};
206 break; 175 else
207 endif 176 error ("odeset: no exact match for '%s'. Possible fields found: %s.",
208 177 name, strjoin (options(match), ", "));
209 ## FIXME: Do we really need interactive selection? 178 endif
210 ## if there are more matching, ask the user to be more precise
211 warning ("OdePkg:InvalidArgument", "no exact matching for '%s'. ",
212 "%d possible fields were found",
213 name, rows (pos));
214 for j = 1:(rows (pos))
215 printf ("%s\n", deblank (fields(pos(j),:)));
216 endfor
217 do
218 disp ("Please insert field name again");
219 name = input ("New field name: ");
220 until (ischar (name))
221 endwhile
222 endfor 179 endfor
223 180
224 ## check if all has been done gives a valid OdePkg struct 181 ## Check if all changes have resulted in a valid OdePkg struct
225 ode_struct_value_check (odestruct); 182 ode_struct_value_check (odestruct);
226 return; 183
184 else
185 ## First input argument was not a struct, must be field/value pairs
186 if (mod (nargin, 2) != 0)
187 error ("odeset: FIELD/VALUE arguments must occur in pairs");
188 elseif (! all (cellfun ("isclass", varargin(1:2:end), "char")))
189 error ("odeset: All FIELD names must be strings");
190 endif
191
192 for i = 1:2:nargin
193 name = lower (varargin{i});
194
195 exactmatch = true;
196 match = find (strcmpi (name, options));
197 if (isempty (match))
198 match = find (strncmpi (name, options, length (name)));
199 exactmatch = false;
200 endif
201
202 if (isempty (match))
203 error ("odeset: invalid property '%s'", varargin{i});
204 elseif (numel (match) == 1)
205 if (! exactmatch)
206 warning ("odeset:NoExactMatching",
207 "no exact match for '%s'. Assuming '%s'.",
208 name, options{match});
209 endif
210 odestruct.(options{match}) = varargin{i+1};
211 else
212 error ("odeset: no exact match for '%s'. Possible fields found: %s.",
213 name, strjoin (options(match), ", "));
214 endif
215 endfor
216
217 ## Check if all changes have resulted in a valid OdePkg struct
218 ode_struct_value_check (odestruct);
219
227 endif 220 endif
228
229 ## first input argument was not a struct
230 if (mod (nargin, 2) != 0)
231 error ("odeset: FIELD/VALUE arguments must occur in pairs");
232 endif
233
234 if (! all (cellfun ("isclass", varargin(1:2:end), "char")))
235 error ("odeset: All FIELD names must be strings");
236 endif
237
238 for i = 1:2:(nargin-1)
239 name = varargin{i};
240
241 while (1)
242 pos = fuzzy_compare (name, fields);
243
244 if (isempty (pos))
245 error ("OdePkg:InvalidArgument",
246 "invalid property. No property found with name '%s'", name);
247 endif
248
249 if (rows (pos) == 1)
250 if (! strcmp (lower (deblank (name)),
251 lower (deblank (fields(pos,:)))))
252 warning ("OdePkg:InvalidArgument", "no exact matching for ",
253 "'%s'. Assuming you were intending '%s'",
254 name, deblank (fields(pos,:)));
255 endif
256 odestruct.(deblank (fields(pos,:))) = varargin{i+1};
257 break;
258 endif
259
260 ## FIXME: Do we really need interactive selection?
261 ## if there are more matching, ask the user to be more precise
262 warning ("OdePkg:InvalidArgument", "no exact matching for '%s'. ",
263 "%d possible fields were found",
264 name, rows (pos));
265 for j = 1:rows (pos)
266 printf ("%s\n", deblank (fields(pos(j),:)));
267 endfor
268 do
269 disp ("Please insert field name again");
270 name = input ("New field name: ");
271 until (ischar (name))
272 endwhile
273 endfor
274
275 ## check if all has been done gives a valid OdePkg struct
276 ode_struct_value_check (odestruct);
277 221
278 endfunction 222 endfunction
279 223
280 ## function useful to print all the possible options 224 ## function useful to print all the possible options
281 function print_options () 225 function print_options ()
282 226
283 disp ("These following are all possible options."); 227 disp ("List of all possible ODE solver options.");
284 disp ("Default values are put in square brackets."); 228 disp ("Default values are in square brackets.");
285 disp (""); 229 disp ("");
286 disp (" AbsTol: scalar or vector, >0, [1e-6]"); 230 disp (" AbsTol: scalar or vector, >0, [1e-6]");
287 disp (" Algorithm: string, {['gmres'], 'pcg', 'bicgstab'}"); 231 disp (" Algorithm: string, {['gmres'], 'pcg', 'bicgstab'}");
288 disp (" BDF: binary, {'on', ['off']}"); 232 disp (" BDF: binary, {'on', ['off']}");
289 disp (" Choice: switch, {[1], 2}"); 233 disp (" Choice: switch, {[1], 2}");
336 %! odeoptB = odeset ("AbsTol", 1e-2, "RelTol", 1e-1); 280 %! odeoptB = odeset ("AbsTol", 1e-2, "RelTol", 1e-1);
337 281
338 %!demo 282 %!demo
339 %! # A new OdePkg options structure is created from odeoptB with 283 %! # A new OdePkg options structure is created from odeoptB with
340 %! # a modified value for option "NormControl". 284 %! # a modified value for option "NormControl".
341 285 %!
342 %! odeoptB = odeset ("AbsTol", 1e-2, "RelTol", 1e-1); 286 %! odeoptB = odeset ("AbsTol", 1e-2, "RelTol", 1e-1);
343 %! odeoptC = odeset (odeoptB, "NormControl", "on"); 287 %! odeoptC = odeset (odeoptB, "NormControl", "on");
344 288
345 ## All tests that are needed to check if a correct resp. valid option 289 ## All tests that are needed to check if a correct resp. valid option
346 ## has been set are implemented in ode_struct_value_check.m. 290 ## has been set are implemented in ode_struct_value_check.m.
347 %!test 291 %!test
348 %! wstate = warning ("off", "OdePkg:InvalidArgument"); 292 %! odeoptA = odeset ();
349 %! unwind_protect 293 %! assert (isstruct (odeoptA));
350 %! odeoptA = odeset (); 294 %! fields = fieldnames (odeoptA);
351 %! ## FIXME: no assert check on odeoptA 295 %! assert (numel (fields), 37);
352 %! odeoptB = odeset ("AbsTol", 1e-2, "RelTol", 1e-1); 296 %! assert (all (structfun ("isempty", odeoptA)));
353 %! assert (odeoptB.AbsTol, 1e-2); 297
354 %! assert (odeoptB.RelTol, 1e-1); 298 %!shared odeoptB, odeoptC
355 %! odeoptC = odeset (odeoptB, "NormControl", "on"); 299 %!test
356 %! ## FIXME: no assert check on odeoptC 300 %! odeoptB = odeset ("ABSTOL", 1e-2, "reltol", 1e-1);
357 %! odeoptD = odeset (odeoptC, odeoptB); 301 %! assert (odeoptB.AbsTol, 1e-2); # Check canonicalization of name
358 %! ## FIXME: no assert check on odeoptD 302 %! assert (odeoptB.RelTol, 1e-1);
359 %! unwind_protect_cleanup 303
360 %! warning (wstate); 304 %!test
361 %! end_unwind_protect 305 %! odeoptC = odeset (odeoptB, "NormControl", "on");
362 306 %! assert (odeoptC.AbsTol, 1e-2); # check values from first struct copied
307 %! assert (odeoptC.NormControl, "on"); # check new values override old ones
308
309 %!test
310 %! odeoptD = odeset (odeoptB, odeoptC);
311 %! assert (odeoptD, odeoptC);
312