Mercurial > octave
view scripts/general/integral2.m @ 24102:c723faa56ab4
Add missing functions integral2.m and integral3.m (bug #52074).
* integral2.m: new function file, primarily a wrapper to dblquad.
* integral3.m: new function file, primarily a wrapper to triplequad.
* scripts/general/module.mk: add integral2.m and integral3.m to build system.
* quad.txi: Add doc reference to integral2 and integral3.
* NEWS: Announce new functions integral2, integral3.
* __unimplemented__.m: remove integral2, integral3 from the list.
* integral.m, dblquad.m, quadl.m, quadv.m, triplequad.m: updated seealso links
in docstring.
* quadgk.m: Updated docstring seealso links and added tolerance note.
author | Nicholas R. Jankowski <jankowskin@asme.org> |
---|---|
date | Sat, 23 Sep 2017 18:11:20 -0400 |
parents | |
children | 2e64bed0bb3a |
line wrap: on
line source
## Copyright (C) 2017 Nicholas Jankowski ## ## 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{q} =} integral2 (@var{f}, @var{xa}, @var{xb}, @var{ya}, @var{yb}) ## @deftypefnx {} {@var{q} =} integral2 (@var{f}, @var{xa}, @var{xb}, @var{ya}, @var{yb}, @var{prop}, @var{val}, @dots{}) ## ## Numerically evaluate the two-dimensional integral of @var{f} using adaptive ## quadrature over the two-dimensional domain defined by @var{xa}, @var{xb}, ## @var{ya}, @var{yb} (scalars may be finite or infinite). ## ## @code{integral2} is a wrapper for @code{dblquad} intended to provide ## @sc{matlab} compatibility. More control of the numerical integration may ## be achievable by calling the various quadrature functions directly. ## ## @var{f} is a function handle, inline function, or string containing the name ## of the function to evaluate. The function @var{f} must be of the form ## @math{z = f(x,y)} where @var{x} is a vector and @var{y} is a scalar. It ## should return a vector of the same length and orientation as @var{x}. ## ## Additional optional parameters can be specified using ## @qcode{"@var{property}", @var{value}} pairs. Valid properties are: ## ## @table @code ## @item AbsTol ## Define the absolute error tolerance for the quadrature. The default ## value is 1e-10 (1e-5 for single). ## ## @item RelTol ## Define the relative error tolerance for the quadrature. The default ## value is 1e-6 (1e-4 for single). ## @end table ## ## Adaptive quadrature is used to minimize the estimate of error until the ## following is satisfied: ## @tex ## $$error \leq \max \left( AbsTol, RelTol\cdot\vert q\vert \right)$$ ## @end tex ## @ifnottex ## ## @example ## @group ## @var{error} <= max (@var{AbsTol}, @var{RelTol}*|@var{q}|). ## @end group ## @end example ## ## @end ifnottex ## ## Known @sc{matlab} incompatibilities: ## ## @enumerate ## @item ## If tolerances are left unspecified, and any integration limits or waypoints ## are of type @code{single}, then Octave's integral functions automatically ## reduce the default absolute and relative error tolerances as specified ## above. If tighter tolerances are desired they must be specified. ## @sc{matlab} leaves the tighter tolerances appropriate for @code{double} ## inputs in place regardless of the class of the integration limits. # ## @item ## @code{integral2} currently only integrates functions over rectangular ## domains. Implementing @var{ya} and @var{yb} as functions of @var{x} is a ## planned future improvement. ## ## @item ## The @qcode{"Method"} property is not yet implemented in Octave due to the ## lack of a @qcode{"tiled"} integrator implementation. All integrals are ## evaluated using an equivalent of the @qcode{"iterated"} method. ## @end enumerate ## ## @seealso{integral, integral3, quad, quadgk, quadv, quadl, quadcc, trapz, ## dblquad, triplequad} ## @end deftypefn function q = integral2 (f, xa, xb, ya, yb, varargin) ## FIXME: It is possible that a non-rectangular domain could be handled by ## overlaying the integrand with a boolean mask function such that ## the integration occurs over a rectangle, but regions outside the ## desired domain contribute zero to the integral. This may be an ## inefficient but acceptable hack to get around the rectangular ## domain limit without having to rewrite the integrating function. ## FIXME: Implement "Method" property to let the user select between iterated ## and tiled integration. Tiled integration follows the method of ## Matlab's quad2d function, currently unimplemented in Octave. ## Should probably just wait for a quad2d implementation to point the ## integral2 wrapper to, instead of trying to re-create it here. The ## following can be added to the help docstring once it is functional: ## @item Method ## Specifies the two dimensional integration method to be used, with valid ## options being @var{"auto"}, @var{"tiled"}, or @var{"iterated"}. ## @code{integral} will use @var{"auto"} by default, where it will usually ## choose @var{"tiled"} unless any of the integration limits are infinite. if (nargin < 5 || (mod (nargin, 2) == 0)) print_usage (); endif if (! is_function_handle (f)) print_usage (); endif if (! (isscalar (xa) && isscalar (xb) && isscalar (ya)) && isscalar (yb)) print_usage (); endif ## Check for single or double limits to set appropriate default tolerance. issingle = isa ([xa, xb, ya, yb], "single"); ## Set defaults, update with any specified parameters. if (issingle) abstol = 1e-5; reltol = 1e-4; else abstol = 1e-10; reltol = 1e-6; endif if (nargin == 5) ## Pass the simplest case directly to integrator. ## Let quadcc function handle input checks. q = dblquad (f, xa, xb, ya, yb, [abstol, reltol], @quadcc); else ## Parse options to determine how to call integrator. intmethod = []; idx = 1; while (idx < nargin - 5) prop = varargin{idx++}; if (! ischar (prop)) error ("integral2: property PROP must be a string"); endif switch (tolower (prop)) case "abstol" abstol = varargin{idx++}; if (! (isnumeric (abstol) && isscalar (abstol) && abstol >= 0)) error ("integral2: AbsTol value must be a numeric scalar >= 0"); endif case "reltol" reltol = varargin{idx++}; if (! (isnumeric (reltol) && isscalar (reltol) && reltol >= 0)) error ("integral2: RelTol value must be a numeric scalar >= 0"); endif case "method" intmethod = varargin{idx++}; warning (["integral2: Only 'iterated' method implemented. ", ... "Method property ignored."]); otherwise error ("integral2: unknown property '%s'", prop); endswitch endwhile q = dblquad (f, xa, xb, ya, yb, [abstol, reltol], @quadcc); endif endfunction %!test %! f = @(x, y) x .* y; %! assert (integral2 (f, 0, 1, 0, 1), 0.25, 1e-10); %!test %! f = @(x, y) 9 * x.^2 + 15 * y.^2; %! assert (integral2 (f, 0, 5, -5, 0, "AbsTol", 1e-9), 5000, 1e-9); %! assert (integral2 (f, 0, 5, -5, 0, "RelTol", 1e-6), 5000, -1e-6); %! assert (integral2 (f, 0, 5, -5, 0, "RelTol", 1e-6, "AbsTol", 1e-9), %! 5000, 1e-9); ## tests from dblquad %!assert (integral2 (@(x, y) 1 ./ (x+y), 0, 1, 0, 1, "AbsTol", 1e-7), %! 2*log (2), 1e-7) %!assert (integral2 (@(x, y) 1 ./ (x+y), 0, 1, 0, 1, "RelTol", 1e-6), %! 2*log (2), -1e-6) %!assert (integral2 (@(x, y) 1 ./ (x+y), 0, 1, 0, 1, "AbsTol", 1e-8, %! "RelTol", 1e-6), 2*log (2), -1e-6) %!assert (integral2 (@(x, y) exp (-x.^2 - y.^2) , -1, 1, -1, 1), %! pi * erf (1).^2, 1e-10) %!assert (integral2 (@plus, 1, 2, 3, 4), 5, 1e-10) ## Test input validation %!error integral2 %!error integral2 (0, 1 ,2 ,3 ,4) %!error integral2 (@plus) %!error integral2 (@plus, 1) %!error integral2 (@plus, 1, 2) %!error integral2 (@plus, 1, 2, 3) %!error integral2 (@plus, 1, 2, 3, [4 5]) %!error integral2 (@plus, 1, 2, 3, "test") %!error integral2 (@plus, 1, 2, 3, 4, "foo") %!error <unknown property 'foo'> integral2 (@plus, 1, 2, 3, 4, "foo", "bar") %!error <property PROP must be a string> integral2 (@plus, 1, 2, 3, 4, 99, "bar") %!error <AbsTol value must be a numeric> integral2 (@plus, 1, 2, 3, 4, "AbsTol", "foo") %!error <AbsTol value must be a .* scalar> integral2 (@plus, 1, 2, 3, 4, "AbsTol", [1, 2]) %!error <AbsTol value must be.* .= 0> integral2 (@plus, 1, 2, 3, 4, "AbsTol", -1) %!error <RelTol value must be a numeric> integral2 (@plus, 1, 2, 3, 4, "RelTol", "foo") %!error <RelTol value must be a .* scalar> integral2 (@plus, 1, 2, 3, 4, "RelTol", [1, 2]) %!error <RelTol value must be.* .= 0> integral2 (@plus, 1, 2, 3, 4, "RelTol", -1) %!warning <Only 'iterated' method implemented> %! q = integral2 (@plus, 0, 1, 0, 1, "Method", "tiled");