Mercurial > octave
changeset 28045:13dba3c069f8
Update input validation for odeXXX.m functions.
* ode15i.m, ode15s.m, ode23.m, ode45.m:
Use name of function in error messages directly rather than interpolating
variable "solver" in to a character matrix. Use double quotes around
field values such as "Jacobian" rather than single quotes. Use intermediate
variables to make the code more readable. Fix incorrect validation and tighten
the acceptance tests for Jacobian and Mass fields. Improve error messages from
generic "invalid argument" to more specifically state what is wrong with the
input.
author | Rik <rik@octave.org> |
---|---|
date | Wed, 05 Feb 2020 09:14:08 -0800 |
parents | ace8dc642e4d |
children | 5664362da646 |
files | scripts/ode/ode15i.m scripts/ode/ode15s.m scripts/ode/ode23.m scripts/ode/ode45.m |
diffstat | 4 files changed, 97 insertions(+), 94 deletions(-) [+] |
line wrap: on
line diff
--- a/scripts/ode/ode15i.m Tue Feb 04 16:29:37 2020 -0800 +++ b/scripts/ode/ode15i.m Wed Feb 05 09:14:08 2020 -0800 @@ -119,7 +119,7 @@ if (ischar (options.Jacobian)) if (! exist (options.Jacobian)) error ("Octave:invalid-input-arg", - [solver ": function '" options.Jacobian "' not found"]); + ['ode15i: "Jacobian" function "' options.Jacobian '" not found']); endif options.Jacobian = str2func (options.Jacobian); endif @@ -129,13 +129,13 @@ if (ischar (options.OutputFcn)) if (! exist (options.OutputFcn)) error ("Octave:invalid-input-arg", - [solver ": function '" options.OutputFcn "' not found"]); + ['ode15i: "OutputFcn" function "' options.OutputFcn '" not found']); endif options.OutputFcn = str2func (options.OutputFcn); endif if (! is_function_handle (options.OutputFcn)) error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'OutputFcn'"]); + 'ode15i: "OutputFcn" must be a valid function handle'); endif endif @@ -143,13 +143,13 @@ if (ischar (options.Events)) if (! exist (options.Events)) error ("Octave:invalid-input-arg", - [solver ": function '" options.Events "' not found"]); + ['ode15i: "Events" function "' options.Events '" not found']); endif options.Events = str2func (options.Events); endif if (! is_function_handle (options.Events)) error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Events'"]); + 'ode15i: "Events" must be a valid function handle'); endif endif @@ -178,45 +178,45 @@ options.havejac = true; if (iscell (options.Jacobian)) if (numel (options.Jacobian) == 2) - if (issparse (options.Jacobian{1}) && issparse (options.Jacobian{2})) - options.havejacsparse = true; # Jac is sparse cell + J1 = options.Jacobian{1}; + J2 = options.Jacobian{2}; + if ( ! issquare (J1) || ! issquare (J2) + || rows (J1) != n || rows (J2) != n + || ! isnumeric (J1) || ! isnumeric (J2) + || ! isreal (J1) || ! isreal (J2)) + error ("Octave:invalid-input-arg", + 'ode15i: "Jacobian" matrices must be real square matrices'); endif - - if (any (size (options.Jacobian{1}) != [n n]) - || any (size (options.Jacobian{2}) != [n n]) - || ! isnumeric (options.Jacobian{1}) - || ! isnumeric (options.Jacobian{2}) - || ! isreal (options.Jacobian{1}) - || ! isreal (options.Jacobian{2})) - error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Jacobian'"]); + if (issparse (J1) && issparse (J2)) + options.havejacsparse = true; # Jac is sparse cell endif else error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Jacobian'"]); + 'ode15i: invalid value assigned to field "Jacobian"'); endif elseif (is_function_handle (options.Jacobian)) options.havejacfun = true; if (nargin (options.Jacobian) == 3) - [A, B] = options.Jacobian (trange(1), y0, yp0); - if (issparse (A) && issparse (B)) - options.havejacsparse = true; # Jac is sparse fun - endif + [J1, J2] = options.Jacobian (trange(1), y0, yp0); - if (any (size (A) != [n n]) || any (size (B) != [n n]) - || ! isnumeric (A) || ! isnumeric (B) || ! isreal (A) - || ! isreal (B)) + if ( ! issquare (J1) || rows (J1) != n + || ! isnumeric (J1) || ! isreal (J1) + || ! issquare (J2) || rows (J2) != n + || ! isnumeric (J2) || ! isreal (J2)) error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Jacobian'"]); + 'ode15i: "Jacobian" function must evaluate to a real square matrix'); + endif + if (issparse (J1) && issparse (J2)) + options.havejacsparse = true; # Jac is sparse fun endif else error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Jacobian'"]); + 'ode15i: invalid value assigned to field "Jacobian"'); endif else - error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Jacobian'"]); + error ("Octave:invalid-input-arg", + 'ode15i: "Jacobian" field must be a function handle or 2-element cell array of square matrices'); endif endif @@ -225,7 +225,7 @@ if (numel (options.AbsTol) != 1 && numel (options.AbsTol) != n) error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'AbsTol'"]); + 'ode15i: invalid value assigned to field "AbsTol"'); elseif (numel (options.AbsTol) == n) options.haveabstolvec = true;
--- a/scripts/ode/ode15s.m Tue Feb 04 16:29:37 2020 -0800 +++ b/scripts/ode/ode15s.m Wed Feb 05 09:14:08 2020 -0800 @@ -113,13 +113,13 @@ if (ischar (options.Mass)) if (! exist (options.Mass)) error ("Octave:invalid-input-arg", - [solver ": function '" options.Mass "' not found"]); + ['ode15s: "Mass" function "' options.Mass '" not found']); endif options.Mass = str2func (options.Mass); endif if (! is_function_handle (options.Mass) && ! isnumeric (options.Mass)) error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Mass'"]); + 'ode15s: "Mass" field must be a function handle or square matrix'); endif endif @@ -127,14 +127,14 @@ if (ischar (options.Jacobian)) if (! exist (options.Jacobian)) error ("Octave:invalid-input-arg", - [solver ": function '" options.Jacobian "' not found"]); + ['ode15s: "Jacobian" function "' options.Jacobian '" not found']); endif options.Jacobian = str2func (options.Jacobian); endif if (! is_function_handle (options.Jacobian) && ! isnumeric (options.Jacobian)) error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Jacobian'"]); + 'ode15s: "Jacobian" field must be a function handle or square matrix'); endif endif @@ -142,13 +142,13 @@ if (ischar (options.OutputFcn)) if (! exist (options.OutputFcn)) error ("Octave:invalid-input-arg", - [solver ": function '" options.OutputFcn "' not found"]); + ['ode15s: "OutputFcn" function "' options.OutputFcn '" not found']); endif options.OutputFcn = str2func (options.OutputFcn); endif if (! is_function_handle (options.OutputFcn)) error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'OutputFcn'"]); + 'ode15s: "OutputFcn" must be a valid function handle'); endif endif @@ -156,13 +156,13 @@ if (ischar (options.Events)) if (! exist (options.Events)) error ("Octave:invalid-input-arg", - [solver ": function '" options.Events "' not found"]); + ['ode15s: "Events" function "' options.Events '" not found']); endif options.Events = str2func (options.Events); endif if (! is_function_handle (options.Events)) error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Events'"]); + 'ode15s: "Events" must be a valid function handle'); endif endif @@ -186,33 +186,30 @@ if (nargin (options.Mass) == 2) options.havestatedep = true; M = options.Mass (trange(1), y0); - options.havemasssparse = issparse (M); - if (any (size (M) != [n n]) || ! isnumeric (M) || ! isreal (M)) + if (! issquare (M) || rows (M) != n || ! isnumeric (M) || ! isreal (M)) error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Mass'"]); + 'ode15s: "Mass" function must evaluate to a real square matrix'); endif + options.havemasssparse = issparse (M); elseif (nargin (options.Mass) == 1) options.havetimedep = true; M = options.Mass (trange(1)); - options.havemasssparse = issparse (M); - if (any (size (M) != [n n]) || ! isnumeric (M) || ! isreal (M)) + if (! issquare (M) || rows (M) != n || ! isnumeric (M) || ! isreal (M)) error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Mass'"]); + 'ode15s: "Mass" function must evaluate to a real square matrix'); endif + options.havemasssparse = issparse (M); else error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Mass'"]); + 'ode15s: invalid value assigned to field "Mass"'); endif - elseif (ismatrix (options.Mass)) - options.havemasssparse = issparse (options.Mass); - if (any (size (options.Mass) != [n n]) + else # matrix Mass input + if (! issquare (options.Mass) || rows (options.Mass) != n || ! isnumeric (options.Mass) || ! isreal (options.Mass)) error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Mass'"]); + 'ode15s: "Mass" matrix must be a real square matrix'); endif - else - error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Mass'"]); + options.havemasssparse = issparse (options.Mass); endif endif @@ -226,29 +223,23 @@ if (is_function_handle (options.Jacobian)) options.havejacfun = true; if (nargin (options.Jacobian) == 2) - [A] = options.Jacobian (trange(1), y0); - if (issparse (A)) - options.havejacsparse = true; # Jac is sparse fun + A = options.Jacobian (trange(1), y0); + if (! issquare (A) || rows (A) != n || ! isnumeric (A) || ! isreal (A)) + error ("Octave:invalid-input-arg", + 'ode15s: "Jacobian" function must evaluate to a real square matrix'); endif - if (any (size (A) != [n n]) || ! isnumeric (A) || ! isreal (A)) - error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Jacobian'"]); - endif + options.havejacsparse = issparse (A); # Jac is sparse fun else error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Jacobian'"]); - endif - elseif (ismatrix (options.Jacobian)) - if (issparse (options.Jacobian)) - options.havejacsparse = true; # Jac is sparse matrix + 'ode15s: invalid value assigned to field "Jacobian"'); endif - if (! issquare (options.Jacobian)) + else # matrix input + if (! issquare (options.Jacobian) || rows (options.Jacobian) != n + || ! isnumeric (options.Jacobian) || ! isreal (options.Jacobian)) error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Jacobian'"]); + 'ode15s: "Jacobian" matrix must be a real square matrix'); endif - else - error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'Jacobian'"]); + options.havejacsparse = issparse (options.Jacobian); endif endif @@ -259,8 +250,8 @@ options.Jacobian = []; warning ("ode15s:mass_state_dependent_provided", ["with MStateDependence != 'none' an internal", ... - " approximation of Jacobian Matrix will be used.", ... - " Set MStateDependence equal to 'none' if you want ", ... + " approximation of the Jacobian Matrix will be used.", ... + " Set MStateDependence equal to 'none' if you want", ... " to provide a constant or time-dependent Jacobian"]); endif endif @@ -290,7 +281,7 @@ if (numel (options.AbsTol) != 1 && numel (options.AbsTol) != n) error ("Octave:invalid-input-arg", - [solver ": invalid value assigned to field 'AbsTol'"]); + 'ode15s: invalid value assigned to field "AbsTol"'); elseif (numel (options.AbsTol) == n) options.haveabstolvec = true; endif
--- a/scripts/ode/ode23.m Tue Feb 04 16:29:37 2020 -0800 +++ b/scripts/ode/ode23.m Wed Feb 05 09:14:08 2020 -0800 @@ -98,8 +98,8 @@ print_usage (); endif + solver = "ode23"; order = 3; - solver = "ode23"; if (nargin >= 4) if (! isstruct (varargin{1})) @@ -110,7 +110,8 @@ ## varargin{1} is an ODE options structure opt odeopts = varargin{1}; funarguments = {varargin{2:numel (varargin)}}; - else # if (isstruct (varargin{1})) + else + ## varargin{1} is an ODE options structure opt odeopts = varargin{1}; funarguments = {}; endif @@ -144,13 +145,13 @@ if (ischar (fun)) if (! exist (fun)) error ("Octave:invalid-input-arg", - [solver ": function '" fun "' not found"]); + ['ode23: function "' fun '" not found']); endif fun = str2func (fun); endif if (! is_function_handle (fun)) error ("Octave:invalid-input-arg", - [solver ": FUN must be a valid function handle"]); + "ode23: FUN must be a valid function handle"); endif ## Start preprocessing, have a look which options are set in odeopts, @@ -199,13 +200,18 @@ odeopts.funarguments); endif - if (! isempty (odeopts.Mass) && isnumeric (odeopts.Mass)) - havemasshandle = false; - mass = odeopts.Mass; # constant mass - elseif (is_function_handle (odeopts.Mass)) - havemasshandle = true; # mass defined by a function handle - else # no mass matrix - creating a diag-matrix of ones for mass - havemasshandle = false; # mass = diag (ones (length (init), 1), 0); + if (! isempty (odeopts.Mass)) + if (isnumeric (odeopts.Mass)) + havemasshandle = false; + mass = odeopts.Mass; # constant mass + elseif (is_function_handle (odeopts.Mass)) + havemasshandle = true; # mass defined by a function handle + else + error ("Octave:invalid-input-arg", + 'ode45: "Mass" field must be a function handle or square matrix'); + endif + else # no mass matrix - create a diag-matrix of ones for mass + havemasshandle = false; # mass = diag (ones (length (init), 1), 0); endif ## Starting the initialization of the core solver ode23 @@ -262,8 +268,8 @@ varargout{1} = solution.t; # Time stamps are first output argument varargout{2} = solution.x; # Results are second output argument elseif (nargout == 1) - varargout{1}.x = solution.t.'; # Time stamps are saved in field x (row vector) - varargout{1}.y = solution.x.'; # Results are saved in field y (row vector) + varargout{1}.x = solution.t.'; # Time stamps saved in field x (row vector) + varargout{1}.y = solution.x.'; # Results are saved in field y (row vector) varargout{1}.solver = solver; # Solver name is saved in field solver if (! isempty (odeopts.Events)) varargout{1}.xe = solution.event{3}; # Time info when an event occurred
--- a/scripts/ode/ode45.m Tue Feb 04 16:29:37 2020 -0800 +++ b/scripts/ode/ode45.m Wed Feb 05 09:14:08 2020 -0800 @@ -95,8 +95,8 @@ print_usage (); endif + solver = "ode45"; order = 5; # runge_kutta_45_dorpri uses local extrapolation - solver = "ode45"; if (nargin >= 4) if (! isstruct (varargin{1})) @@ -107,7 +107,8 @@ ## varargin{1} is an ODE options structure opt odeopts = varargin{1}; funarguments = {varargin{2:numel (varargin)}}; - else # if (isstruct (varargin{1})) + else + ## varargin{1} is an ODE options structure opt odeopts = varargin{1}; funarguments = {}; endif @@ -141,13 +142,13 @@ if (ischar (fun)) if (! exist (fun)) error ("Octave:invalid-input-arg", - [solver ": function '" fun "' not found"]); + ['ode45: function "' fun '" not found']); endif fun = str2func (fun); endif if (! is_function_handle (fun)) error ("Octave:invalid-input-arg", - [solver ": FUN must be a valid function handle"]); + "ode45: FUN must be a valid function handle"); endif ## Start preprocessing, have a look which options are set in odeopts, @@ -199,12 +200,17 @@ odeopts.funarguments); endif - if (! isempty (odeopts.Mass) && isnumeric (odeopts.Mass)) - havemasshandle = false; - mass = odeopts.Mass; # constant mass - elseif (is_function_handle (odeopts.Mass)) - havemasshandle = true; # mass defined by a function handle - else # no mass matrix - creating a diag-matrix of ones for mass + if (! isempty (odeopts.Mass)) + if (isnumeric (odeopts.Mass)) + havemasshandle = false; + mass = odeopts.Mass; # constant mass + elseif (is_function_handle (odeopts.Mass)) + havemasshandle = true; # mass defined by a function handle + else + error ("Octave:invalid-input-arg", + 'ode45: "Mass" field must be a function handle or square matrix'); + endif + else # no mass matrix - create a diag-matrix of ones for mass havemasshandle = false; # mass = diag (ones (length (init), 1), 0); endif