diff scripts/ode/ode15s.m @ 22901:4c56f3ffec04

Add functions ode15i and ode15s * libinterp/dldfcn/__ode15__.cc: Add oct-file backend for m-files ode15i.m and ode15s.m. * scripts/ode/ode15i.m: Add wrapper function ode15i.m. * scripts/ode/ode15s.m: Add wrapper function ode15s.m. * scripts/ode/private/check_default_input.m: Add private function to check default input of ode15i and ode15s. * libinterp/dldfcn/module-files: Add __ode15__.cc and its flags.
author Francesco Faccio <francesco.faccio@mail.polimi.it>
date Tue, 23 Aug 2016 03:19:11 +0200
parents
children 284bbb0328f2
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/ode/ode15s.m	Tue Aug 23 03:19:11 2016 +0200
@@ -0,0 +1,686 @@
+## Copyright (C) 2016, Francesco Faccio <francesco.faccio@mail.polimi.it>
+##
+## This file is part of Octave.
+##
+## Octave is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## Octave is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {} {[@var{t}, @var{y}] =} ode15s (@var{fun}, @var{trange}, @var{y0})
+## @deftypefnx {} {[@var{t}, @var{y}] =} ode15s (@var{fun}, @var{trange}, @var{y0}, @var{ode_opt})
+## @deftypefnx {} {[@var{t}, @var{y}, @var{te}, @var{ye}, @var{ie}] =} ode15s (@dots{})
+## @deftypefnx {} {@var{solution} =} ode15s (@dots{})
+##
+## Solve a set of stiff Ordinary Differential Equations and stiff semi-explicit
+## Differential Algebraic Equations (DAEs) of index 1, with the variable-step,
+## variable order BDF (Backward Differentiation Formula) method, which
+## ranges from order 1 to 5.
+##
+## @var{fun} is a function handle, inline function, or string containing the
+## name of the function that defines the ODE: @code{f(@var{t},@var{y})}.
+## The function must accept two inputs where the first is time @var{t} and the
+## second is a column vector of unknowns @var{y}.
+##
+## @var{trange} specifies the time interval over which the ODE will be
+## evaluated.  Typically, it is a two-element vector specifying the initial and
+## final times (@code{[tinit, tfinal]}).  If there are more than two elements
+## then the solution will also be evaluated at these intermediate time.
+##
+## @var{init} contains the initial value for the unknowns.  If it is a row
+## vector then the solution @var{y} will be a matrix in which each column is
+## the solution for the corresponding initial value in @var{init}.
+##
+## The optional fourth argument @var{ode_opt} specifies non-default options to
+## the ODE solver.  It is a structure generated by @code{odeset}.
+##
+## The function typically returns two outputs.  Variable @var{t} is a
+## column vector and contains the times where the solution was found.  The
+## output @var{y} is a matrix in which each column refers to a different
+## unknown of the problem and each row corresponds to a time in @var{t}.
+##
+## The output can also be returned as a structure @var{solution} which
+## has field @var{x} containing the time where the solution was evaluated and
+## field @var{y} containing the solution matrix for the times in @var{x}.
+## Use @code{fieldnames (@var{solution})} to see the other fields and
+## additional information returned.
+##
+## If using the @qcode{"Events"} option then three additional outputs may
+## be returned.  @var{te} holds the time when an Event function returned a
+## zero.  @var{ye} holds the value of the solution at time @var{te}.  @var{ie}
+## contains an index indicating which Event function was triggered in the case
+## of multiple Event functions.
+##
+## This function can be called with two output arguments: @var{t} and @var{y}.
+## Variable @var{t} is a column vector and contains the time stamps, instead
+## @var{y} is a matrix in which each column refers to a different unknown of
+## the problem and the rows number is the same of @var{t} rows number so
+## that each row of @var{y} contains the values of all unknowns at the time
+## value contained in the corresponding row in @var{t}.
+##
+## Example: Solve the @nospell{Robetson}'s equations:
+## @example
+## @group
+## function res = robertsidae(@var{t}, @var{y})
+## res = [-0.04*@var{y}(1) + 1e4*@var{y}(2)*@var{y}(3);
+##         0.04*@var{y}(1) - 1e4*@var{y}(2)*@var{y}(3) - 3e7*@var{y}(2)^2;
+##         @var{y}(1) + @var{y}(2) + @var{y}(3) - 1];
+## endfunction
+## opt = odeset ("Mass", [1 0 0; 0 1 0; 0 0 0], "MStateDependence", "none");
+## [@var{t},@var{y}] = ode15s (@@robertsidae, [0 1e3], [1; 0; 0], opt);
+## @end group
+## @end example
+## @seealso{decic, odeset, odeget}
+## @end deftypefn
+
+function varargout = ode15s (fun, trange, y0, varargin)
+
+  solver = 'ode15s';  
+ 
+  if (nargin < 3)
+    print_usage ();
+  endif
+  
+  ## Check fun, trange, y0, yp0
+  fun = check_default_input (fun, trange, solver, y0);
+  
+  n = numel (y0);
+  
+  if (nargin > 3)
+   options = varargin{1};
+  else
+   options = odeset ();
+  endif
+
+  if (! isempty (options.Mass))
+    if (ischar (options.Mass))
+      try
+        options.Mass = str2func (options.Mass);
+      catch
+        warning (lasterr);
+      end_try_catch
+      if (! isa (options.Mass, "function_handle"))
+        error ("Octave:invalid-input-arg",
+               [solver ": invalid value assigned to field '%s'"], "Mass");
+      endif
+    endif
+  endif  
+
+  if (! isempty (options.Jacobian))
+    if (ischar (options.Jacobian))
+      try
+        options.Jacobian = str2func (options.Jacobian);
+      catch
+        warning (lasterr);
+      end_try_catch
+      if (! isa (options.Jacobian, "function_handle"))
+        error ("Octave:invalid-input-arg",
+               [solver ": invalid value assigned to field '%s'"], "Jacobian");
+      endif
+    endif
+  endif
+
+  if (! isempty (options.OutputFcn))
+    if (ischar (options.OutputFcn))
+      try
+        options.OutputFcn = str2func (options.OutputFcn);
+      catch
+        warning (lasterr);
+      end_try_catch
+      if (! isa (options.OutputFcn, "function_handle"))
+        error ("Octave:invalid-input-arg",
+               [solver ": invalid value assigned to field '%s'"], "OutputFcn");
+      endif
+    endif
+  endif
+
+  if (! isempty (options.Events))
+    if (ischar (options.Events))
+      try
+        options.Events = str2func (options.Events);
+      catch
+        warning (lasterr);
+      end_try_catch
+      if (! isa (options.Events, "function_handle"))
+        error ("Octave:invalid-input-arg",
+               [solver ": invalid value assigned to field '%s'"], "Events");
+      endif
+    endif
+  endif
+
+  persistent defaults   = [];
+  persistent classes    = [];
+  persistent attributes = [];
+
+  [defaults, classes, attributes] = odedefaults (n, trange(1),
+                                                 trange(end));
+
+  classes    = odeset (classes, 'Vectorized', {});
+  attributes = odeset (attributes, 'Jacobian', {}, 'Vectorized', {});
+
+  options = odemergeopts (options, defaults, classes, attributes, solver);
+
+  ## Mass
+
+  options.havemassfun    = false;
+  options.havestatedep   = false;
+  options.havetimedep    = false;
+  options.havemasssparse = false;
+
+  if (! isempty (options.Mass))
+    if (isa (options.Mass, "function_handle"))  
+      options.havemassfun = true;
+      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))
+          error ("Octave:invalid-input-arg",
+               [solver ": invalid value assigned to field '%s'"], "Mass");
+        endif
+      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))
+          error ("Octave:invalid-input-arg",
+               [solver ": invalid value assigned to field '%s'"], "Mass");
+        endif
+      else
+        error ("Octave:invalid-input-arg",
+                [solver ": invalid value assigned to field '%s'"], "Mass");
+      endif
+    elseif (ismatrix (options.Mass))
+      options.havemasssparse = issparse (options.Mass);
+      if (any (size (options.Mass) != [n n]) ||
+          ! isnumeric (options.Mass) || ! isreal (options.Mass))
+        error ("Octave:invalid-input-arg",
+                [solver ": invalid value assigned to field '%s'"], "Mass");
+      endif
+    else 
+      error ("Octave:invalid-input-arg",
+              [solver ": invalid value assigned to field '%s'"], "Mass");
+    endif
+  endif
+
+
+  ## Jacobian
+  options.havejac       = false;
+  options.havejacsparse = false;
+  options.havejacfun    = false;
+
+  if (! isempty (options.Jacobian))
+    options.havejac = true;
+    if (isa (options.Jacobian, "function_handle"))  
+      options.havejacfun = true;
+      if (nargin (options.Jacobian) == 2)
+        [A] = options.Jacobian (trange(1), y0);
+        if (issparse (A))
+          options.havejacsparse = true;  ## Jac is sparse fun
+        endif
+        if (any (size (A) != [n n]) || ! isnumeric (A) || ! isreal (A))
+          error ("Octave:invalid-input-arg",
+               [solver ": invalid value assigned to field '%s'"], "Jacobian");
+        endif
+      else
+        error ("Octave:invalid-input-arg",
+               [solver ": invalid value assigned to field '%s'"], "Jacobian");
+      endif
+    elseif (ismatrix (options.Jacobian))
+      if (issparse (options.Jacobian))
+        options.havejacsparse = true;            ## Jac is sparse matrix
+      endif
+      if (! issquare (options.Jacobian))
+        error ("Octave:invalid-input-arg",
+               [solver ": invalid value assigned to field '%s'"], "Jacobian");
+      endif
+    else
+      error ("Octave:invalid-input-arg",
+             [solver ": invalid value assigned to field '%s'"], "Jacobian");
+    endif
+  endif
+
+
+  ## Derivative of M(t,y) for implicit problem not implemented yet
+  if (! isempty (options.Mass) && ! isempty (options.Jacobian))
+    if (options.MStateDependence != "none" || options.havestatedep == true)
+      options.havejac = false;
+      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 ", ...
+               " to provide a constant or time-dependent Jacobian"]);
+    endif
+  endif
+  
+  ## Use sparse methods only if all matrices are sparse
+  if (! options.havemasssparse)
+    options.havejacsparse = false; 
+  endif       
+  
+  ## If Mass or Jacobian is fun, then new Jacobian is fun  
+  if (options.havejac)
+    if (options.havejacfun || options.havetimedep)
+      options.Jacobian = @ (t, y, yp) wrapjacfun (t, y, yp,
+                                                  options.Jacobian,
+                                                  options.Mass,
+                                                  options.havetimedep,
+                                                  options.havejacfun);
+      options.havejacfun = true;
+    else   ## All matrices are constant
+      options.Jacobian = {[- options.Jacobian], [options.Mass]};
+           
+    endif
+  endif
+
+
+
+  ## Abstol and Reltol
+
+  options.haveabstolvec = false;
+
+  if (numel (options.AbsTol) != 1 && numel (options.AbsTol) != n)
+    error ("Octave:invalid-input-arg",
+           [solver ": invalid value assigned to field '%s'"], "AbsTol");
+  
+  elseif (numel (options.AbsTol) == n)
+    options.haveabstolvec = true;
+  endif
+
+  ## Stats
+  options.havestats = false;    
+  if (strcmp (options.Stats, "on"))
+    options.havestats = true;    
+  endif
+
+  ## Don't use Refine when the output is a structure
+  if (nargout == 1)
+    options.Refine = 1;
+  endif
+
+  ## OutputFcn and OutputSel
+  if (isempty (options.OutputFcn) && nargout == 0)
+    options.OutputFcn = @odeplot;
+    options.haveoutputfunction = true;
+  else
+    options.haveoutputfunction = ! isempty (options.OutputFcn);
+  endif
+  
+  options.haveoutputselection = ! isempty (options.OutputSel);
+  if (options.haveoutputselection)
+    options.OutputSel = options.OutputSel - 1;
+  endif
+
+  ## Events
+  options.haveeventfunction = ! isempty (options.Events);
+
+  yp0 = options.InitialSlope;
+
+
+  [t, y, te, ye, ie] = __ode15__ (@ (t, y, yp) wrap (t, y, yp, options.Mass,
+                                                     options.havetimedep,
+                                                     options.havestatedep,
+                                                     fun),
+                                  trange, y0, yp0, options);
+
+  if (nargout == 2)
+    varargout{1} = t;
+    varargout{2} = y;
+  elseif (nargout == 1)
+    varargout{1}.x = t;    # Time stamps are saved in field x
+    varargout{1}.y = y;    # Results are saved in field y
+    varargout{1}.solver = solver; 
+    if (options.haveeventfunction)
+      varargout{1}.xe = te;  # Time info when an event occurred
+      varargout{1}.ye = ye;  # Results when an event occurred
+      varargout{1}.ie = ie;  # Index info which event occurred
+    endif
+  elseif (nargout == 5)
+    varargout = cell (1,5);
+    varargout{1} = t;
+    varargout{2} = y;
+    if (options.haveeventfunction)
+      varargout{3} = te;  # Time info when an event occurred
+      varargout{4} = ye;  # Results when an event occurred
+      varargout{5} = ie;  # Index info which event occurred
+    endif
+  endif
+
+endfunction
+
+function res = wrap (t, y, yp, Mass, havetimedep, havestatedep, fun)
+
+  if (! isempty (Mass) && havestatedep)  
+    res = Mass (t, y) * yp - fun (t, y);
+  elseif (! isempty (Mass) && havetimedep)
+    res = Mass (t) * yp - fun (t, y);
+  elseif (! isempty (Mass))
+    res = Mass * yp - fun (t, y);
+  else
+    res = yp - fun (t, y);
+  endif
+
+endfunction
+
+function [jac, jact] = wrapjacfun (t, y, yp, Jac, Mass,
+                                   havetimedep, havejacfun) 
+  if (havejacfun)
+    jac = - Jac (t, y);
+  else
+    jac = - Jac;
+  endif
+
+  if (! isempty (Mass) && havetimedep) 
+    jact = Mass (t);
+  elseif (! isempty (Mass))
+    jact = Mass;
+  else
+    jact = speye (numel (y));
+  endif
+  
+endfunction
+
+
+%!demo
+%! 
+%! ##Solve Robertson's equations with ode15s 
+%! fun = @ (t, y) [-0.04*y(1) + 1e4*y(2).*y(3);
+%!                  0.04*y(1) - 1e4*y(2).*y(3) - 3e7*y(2).^2;
+%!                  y(1) + y(2) + y(3) - 1 ];
+%!
+%! y0 = [1; 0; 0];
+%! tspan = [0 4*logspace(-6, 6)];
+%! M = [1 0 0; 0 1 0; 0 0 0];
+%!  
+%! options = odeset ('RelTol', 1e-4, 'AbsTol', [1e-6 1e-10 1e-6],
+%!                   'MStateDependence', 'none', 'Mass', M);
+%!
+%! [t, y] = ode15s (fun, tspan, y0, options);
+%!
+%! y (:,2) = 1e4 * y (:,2);
+%! figure (2);
+%! semilogx (t, y, 'o')
+%! xlabel ('time');
+%! ylabel ('species concentration');
+%! title ('Robertson DAE problem with a Conservation Law');
+%! legend ('y1', 'y2', 'y3');
+
+%!function ydot = fpol (t, y)  # The Van der Pol
+%! ydot = [y(2); (1 - y(1)^2) * y(2) - y(1)];
+%!endfunction
+%!function ref = fref ()       # The computed reference sol
+%! ref = [0.32331666704577, -1.83297456798624];
+%!endfunction
+%!function jac = fjac (t, y)  # its Jacobian
+%! jac = [0, 1; -1 - 2 * y(1) * y(2), 1 - y(1)^2];
+%!endfunction
+%!function jac = fjcc (t, y)  # sparse type
+%! jac = sparse ([0, 1; -1 - 2 * y(1) * y(2), 1 - y(1)^2]);
+%!endfunction
+%!function mas = fmas (t, y)
+%! mas = [1, 0; 0, 1];             # Dummy mass matrix for tests
+%!endfunction
+%!function mas = fmsa (t, y)
+%! mas = sparse ([1, 0; 0, 1]);    # A sparse dummy matrix
+%!endfunction
+%!function res = rob (t, y)
+%! res = [-0.04*y(1) + 1e4*y(2).*y(3);
+%!         0.04*y(1) - 1e4*y(2).*y(3) - 3e7*y(2).^2;
+%!         y(1) + y(2) + y(3) - 1 ];
+%!endfunction
+%!
+%!function refrob = frefrob()
+%! refrob = [100, 0.617234887614937, 0.000006153591397, 0.382758958793666];
+%!endfunction
+%!function [val, isterminal, direction] = feve (t, y)
+%!  isterminal = [0 1];
+%!  if (t < 1e1)
+%!    val = [-1, -2];
+%!  else
+%!    val = [1 3];
+%!  endif
+%!  
+%!  direction = [1 0];
+%!endfunction
+%!function masrob = massdensefunstate (t, y)
+%! masrob = [1 0 0; 0 1 0; 0 0 0];     
+%!endfunction
+%!function masrob = masssparsefunstate (t, y)
+%! masrob = sparse([1 0 0; 0 1 0; 0 0 0]);     
+%!endfunction
+%!function masrob = massdensefuntime (t)
+%! masrob = [1 0 0; 0 1 0; 0 0 0];     
+%!endfunction
+%!function masrob = masssparsefuntime (t)
+%! masrob = sparse([1 0 0; 0 1 0; 0 0 0]);     
+%!endfunction
+%!function jac = jacfundense (t, y)
+%!  jac = [-0.04,           1e4*y(3),  1e4*y(2);
+%!           0.04, -1e4*y(3)-6e7*y(2), -1e4*y(2);
+%!              1,                  1,         1];
+%!endfunction
+%!function jac = jacfunsparse (t, y)
+%!  jac = sparse([-0.04,           1e4*y(3),  1e4*y(2);
+%!                 0.04, -1e4*y(3)-6e7*y(2), -1e4*y(2);
+%!                    1,                  1,         1]);
+%!endfunction
+
+
+
+%!test
+%! opt = odeset ("MStateDependence", "none", 
+%!                "Mass", [1 0 0; 0 1 0; 0 0 0]);
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);
+%!test
+%! opt = odeset ("MStateDependence", "none", 
+%!                "Mass", sparse([1 0 0; 0 1 0; 0 0 0]));
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);
+%!test
+%! opt = odeset ("MStateDependence", "none", 
+%!                "Mass", @massdensefunstate);
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);
+%!test
+%! opt = odeset ("MStateDependence", "none", 
+%!                "Mass", @masssparsefunstate);
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);
+%!test
+%! opt = odeset ("MStateDependence", "none", 
+%!                "Mass", 'massdensefuntime');
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);  
+%!test 
+%!  opt = odeset ("MStateDependence", "none", 
+%!                "Mass", [1 0 0; 0 1 0; 0 0 0],
+%!                "Jacobian", 'jacfundense');
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);
+%!test
+%! opt = odeset ("MStateDependence", "none", 
+%!                "Mass", sparse([1 0 0; 0 1 0; 0 0 0]),
+%!                "Jacobian", @jacfundense); 
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);
+%!test              
+%! warning ("off", "ode15s:mass_state_dependent_provided", "local");
+%!  opt = odeset ("MStateDependence", "none", 
+%!                "Mass", @massdensefunstate,
+%!                "Jacobian", @jacfundense);
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);
+%!test
+%! warning ("off", "ode15s:mass_state_dependent_provided", "local");
+%!  opt = odeset ("MStateDependence", "none", 
+%!                "Mass", @masssparsefunstate,
+%!                "Jacobian", @jacfundense);
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);
+%!test
+%!  opt = odeset ("MStateDependence", "none", 
+%!                "Mass", @massdensefuntime,
+%!                "Jacobian", @jacfundense);
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);    
+%!  opt = odeset ("MStateDependence", "none", 
+%!                "Mass", 'masssparsefuntime',
+%!                "Jacobian", 'jacfundense');
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);  
+%!test              
+%!  opt = odeset ("MStateDependence", "none", 
+%!                "Mass", [1 0 0; 0 1 0; 0 0 0],
+%!                "Jacobian", @jacfunsparse);
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);
+%!test
+%!  opt = odeset ("MStateDependence", "none", 
+%!                "Mass", sparse([1 0 0; 0 1 0; 0 0 0]),
+%!               "Jacobian", @jacfunsparse); 
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3); 
+%!test             
+%! warning ("off", "ode15s:mass_state_dependent_provided", "local");
+%!  opt = odeset ("MStateDependence", "none", 
+%!                "Mass", @massdensefunstate,
+%!                "Jacobian", @jacfunsparse);
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);
+%!test
+%! warning ("off", "ode15s:mass_state_dependent_provided", "local");
+%!  opt = odeset ("MStateDependence", "none", 
+%!                "Mass", @masssparsefunstate,
+%!                "Jacobian", @jacfunsparse);
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);
+%!test
+%!  opt = odeset ("MStateDependence", "none", 
+%!                "Mass", @massdensefuntime,
+%!                "Jacobian", @jacfunsparse);  
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3); 
+%!test 
+%!  opt = odeset ("MStateDependence", "none", 
+%!                "Mass", @masssparsefuntime,
+%!                "Jacobian", @jacfunsparse);
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), y(end,:)], frefrob, 1e-3);
+%!test  # two output arguments
+%! [t, y] = ode15s (@fpol, [0 2], [2 0]);
+%! assert ([t(end), y(end,:)], [2, fref], 1e-2);
+%!test  # anonymous function instead of real function
+%! fvdb = @(t,y) [y(2); (1 - y(1)^2) * y(2) - y(1)];
+%! [t, y] = ode15s (fvdb, [0 2], [2 0]);
+%! assert ([t(end), y(end,:)], [2, fref], 1e-2);
+%!test  # Solve another anonymous function below zero
+%! ref = [0, 14.77810590694212];
+%! [t, y] = ode15s (@(t,y) y, [-2 0], 2);
+%! assert ([t(end), y(end,:)], ref, 5e-2);
+%!test  # InitialStep option
+%! opt = odeset ("InitialStep", 1e-8);
+%! [t, y] = ode15s (@fpol, [0 0.2], [2 0], opt);
+%! assert ([t(2)-t(1)], [1e-8], 1e-9);
+%!test  # MaxStep option
+%! opt = odeset ("MaxStep", 1e-3);
+%! sol = ode15s (@fpol, [0 0.2], [2 0], opt);
+%! assert ([sol.x(5)-sol.x(4)], [1e-3], 1e-3);
+%!test  # Solve in backward direction starting at t=0
+%! ref = [-1.205364552835178, 0.951542399860817];
+%! sol = ode15s (@fpol, [0 -2], [2 0]);
+%! assert ([sol.x(end), sol.y(end,:)], [-2, ref], 5e-3);
+%!test  # Solve in backward direction starting at t=2
+%! ref = [-1.205364552835178, 0.951542399860817];
+%! sol = ode15s (@fpol, [2 0 -2], fref);
+%! assert ([sol.x(end), sol.y(end,:)], [-2, ref], 3e-2);
+%!test  # Solve another anonymous function in backward direction
+%! ref = [-1, 0.367879437558975];
+%! sol = ode15s (@(t,y) y, [0 -1], 1);
+%! assert ([sol.x(end), sol.y(end,:)], ref, 1e-2);
+%!test  # Solve another anonymous function below zero
+%! ref = [0, 14.77810590694212];
+%! sol = ode15s (@(t,y) y, [-2 0], 2);
+%! assert ([sol.x(end), sol.y(end,:)], ref, 5e-2);
+%!test  # Solve in backward direction starting at t=0 with MaxStep option
+%! ref = [-1.205364552835178, 0.951542399860817];
+%! opt = odeset ("MaxStep", 1e-3);
+%! sol = ode15s (@fpol, [0 -2], [2 0], opt);
+%! assert ([abs(sol.x(8)-sol.x(7))], [1e-3], 1e-3);
+%! assert ([sol.x(end), sol.y(end,:)], [-2, ref], 1e-3);
+%!test  # AbsTol option
+%! opt = odeset ("AbsTol", 1e-5);
+%! sol = ode15s (@fpol, [0 2], [2 0], opt);
+%! assert ([sol.x(end), sol.y(end,:)], [2, fref], 4e-3);
+%!test  # AbsTol and RelTol option
+%! opt = odeset ("AbsTol", 1e-8, "RelTol", 1e-8);
+%! sol = ode15s (@fpol, [0 2], [2 0], opt);
+%! assert ([sol.x(end), sol.y(end,:)], [2, fref], 1e-3);
+%!test  # RelTol option -- higher accuracy
+%! opt = odeset ("RelTol", 1e-8);
+%! sol = ode15s (@fpol, [0 2], [2 0], opt);
+%! assert ([sol.x(end), sol.y(end,:)], [2, fref], 1e-4);
+%!test  # Mass option as function
+%! opt = odeset ("Mass", @fmas, "MStateDependence", "none");
+%! sol = ode15s (@fpol, [0 2], [2 0], opt);
+%! assert ([sol.x(end), sol.y(end,:)], [2, fref], 3e-3);
+%!test  # Mass option as matrix
+%! opt = odeset ("Mass", eye (2,2), "MStateDependence", "none");
+%! sol = ode15s (@fpol, [0 2], [2 0], opt);
+%! assert ([sol.x(end), sol.y(end,:)], [2, fref], 3e-3);
+%!test  # Mass option as sparse matrix
+%! opt = odeset ("Mass", sparse (eye (2,2)), "MStateDependence", "none");
+%! sol = ode15s (@fpol, [0 2], [2 0], opt);
+%! assert ([sol.x(end), sol.y(end,:)], [2, fref], 3e-3);
+%!test  # Mass option as function and sparse matrix
+%! opt = odeset ("Mass", 'fmsa', "MStateDependence", "none");
+%! sol = ode15s (@fpol, [0 2], [2 0], opt);
+%! assert ([sol.x(end), sol.y(end,:)], [2, fref], 3e-3);
+%!test  # Refine
+%! opt2 = odeset ("Refine", 3, "Mass", @massdensefunstate,
+%!                "MStateDependence", "none");
+%! opt1 = odeset ("Mass", @massdensefunstate, "MStateDependence", "none");
+%! [t, y] = ode15s (@rob,[0 100], [1;0;0], opt1);
+%! [t2, y2] = ode15s (@rob,[0 100], [1;0;0], opt2);
+%! assert ([numel(t2)], numel(t)*3, 3);
+%!test  # Refine ignored if numel (trange) > 2
+%! opt2 = odeset ("Refine", 3, "Mass", 'massdensefunstate',
+%!                "MStateDependence", "none");
+%! opt1 = odeset ("Mass", @massdensefunstate, "MStateDependence", "none");
+%! [t, y] = ode15s ('rob',[0 10 100], [1;0;0], opt1);
+%! [t2, y2] = ode15s ('rob',[0 10 100], [1;0;0], opt2);
+%! assert ([numel(t2)], numel(t));
+%!test  # Events option add further elements in sol
+%! opt = odeset ("Events", @feve, "Mass", @massdensefunstate,
+%!               "MStateDependence", "none");
+%! sol = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert (isfield (sol, "ie"));
+%! assert (sol.ie, [0;1]);
+%! assert (isfield (sol, "xe"));
+%! assert (isfield (sol, "ye"));
+%! assert (sol.x(end), 10, 1);
+%!test  # Events option, five output arguments
+%! opt = odeset ("Events", @feve, "Mass", @massdensefunstate,
+%!               "MStateDependence", "none");
+%! [t, y, te, ye, ie] = ode15s (@rob,[0 100], [1;0;0], opt);
+%! assert ([t(end), te', ie'], [10, 10, 10, 0, 1], [1, 0.5, 0.5, 0, 0]); 
+
+
+
+
+
+
+
+
+