Mercurial > octave-nkf
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 |